Zenn
Closed25

Rust の練習帳 メモ

dak2dak2

Struct の field に省略記法が使えるのか

    let bytes = matches.value_of("bytes")
        .map(parse_positive_int)
        .transpose()
        .map_err(|e| format!("illegal byte count -- : {}", e))?;

    Ok(Config {
      files: matches.values_of_lossy("files").unwrap(),
      lines: lines.unwrap(),
      bytes,
    })
dak2dak2
    let lines = matches.value_of("lines")
        .map(|line| parse_positive_int(line))
        .transpose()
        .map_err(|e| format!("illegal line count -- : {}", e))?;

↓map に直接 function 渡せるのか。原理が分かっていない

    let lines = matches.value_of("lines")
        .map(parse_positive_int)
        .transpose()
        .map_err(|e| format!("illegal line count -- : {}", e))?;
dak2dak2

アンダースコア

  • コンパイラに値を使いたくないことを伝える
  • match アームではあらゆるケースにマッチするワイルドカード
dak2dak2

Option 型の Some から値を取り出した上で評価できる
下の例だと config.bytes が Option<usize> であり、None でないことを確認できたら、num_bytes に usize を代入してブロックスコープ内で利用可能

https://doc.rust-lang.org/std/keyword.if.html

        if let Some(num_bytes) = config.bytes {
          let mut handle = file.take(num_bytes as u64);
          let mut buffer = vec![0; num_bytes];
          let bytes_read = handle.read(&mut buffer)?;
          print!(
            "{}",
            String::from_utf8_lossy(&buffer[..bytes_read])
          )
        }
dak2dak2
fn gen_config(
  files: Vec<String>,
  mut lines: bool,
  mut words: bool,
  mut bytes: bool,
  mut chars: bool,
) -> MyResult<Config> {
 
  if lines {
    lines = true;
    words = false;
    bytes = false;
    chars = false;
  }

  if words {
    lines = false;
    words = true;
    bytes = false;
    chars = false;
  }

  if bytes {
    lines = false;
    words = false;
    bytes = true;
    chars = false;
  }

  if chars {
    lines = false;
    words = false;
    bytes = false;
    chars = true;
  }

  if !lines && !words && !bytes && !chars {
    lines = false;
    words = false;
    bytes = false;
    chars = true;
  }

  Ok(Config {
      files,
      lines,
      words,
      bytes,
      chars,
  })
}
fn gen_config(
  files: Vec<String>,
  lines: bool,
  words: bool,
  bytes: bool,
  chars: bool,
) -> MyResult<Config> {

  let (lines, words, bytes, chars) = match (lines, words, bytes, chars) {
      (true, _, _, _) => (true, false, false, false),
      (_, true, _, _) => (false, true, false, false),
      (_, _, true, _) => (false, false, true, false),
      (_, _, _, true) => (false, false, false, true),
      _ => (false, false, false, true),
  };

  Ok(Config {
      files,
      lines,
      words,
      bytes,
      chars,
  })
}

パターンマッチでこのように書き換えられるのか

dak2dak2

5章くらいまで Copilot と進めてきて、意識的に Option を unwrap したり question operator を使ったりと Result 周りに慣れてきた感覚がある

dak2dak2

ライフタイム

その参照が有効になるスコープ

ダングリング参照

参照するつもりだったデータ以外のデータを参照してしまう

    {
        let r;

        {
            let x = 5;
            r = &x;
        }

        println!("r: {}", r);
    }
$ cargo run
   Compiling chapter10 v0.1.0 (file:///projects/chapter10)
error[E0597]: `x` does not live long enough
(エラー[E0597]: `x`の生存期間が短すぎます)
  --> src/main.rs:7:17
   |
7  |             r = &x;
   |                 ^^ borrowed value does not live long enough
   |                   (借用された値の生存期間が短すぎます)
8  |         }
   |         - `x` dropped here while still borrowed
   |          (`x`は借用されている間にここでドロップされました)
9  | 
10 |         println!("r: {}", r);
   |                           - borrow later used here
   |                            (その後、借用はここで使われています)

error: aborting due to previous error

For more information about this error, try `rustc --explain E0597`.
error: could not compile `chapter10`.

To learn more, run the command again with --verbose.

=> 変数 x は借用されているがブロックを抜ける時にドロップされてしまっているにも関わらず、r で参照しようとしている

https://doc.rust-jp.rs/book-ja/ch10-03-lifetime-syntax.html

dak2dak2

`static アノテーションをつけると、参照が指し示す値がプログラムの実行中に渡って生き続ける

dak2dak2

所有権(ownership)

プログラムがリソースを所有する権利

  • 各値それぞれに1つ所有権を持っている
  • スコープから外れたら値は破棄される

ムーブ(move)

所有権が移動すること

#![allow(unused)]
fn main() {
    let s = String::from("hello");  // sがスコープに入る

    takes_ownership(s);             // sの値が関数にムーブされ s の所有権を失う
    
    println!("let s: {}", s)
}
 
fn takes_ownership(some_string: String) { // some_stringがスコープに入る。
    println!("some_string: {}", some_string); // スコープを抜けると some_string が drop
}

上記コードはエラーになる

  1. takes_ownership 関数に s の所有権がムーブされる
  2. takes_ownership 関数のスコープを抜けると所有権を持っていた値が破棄(drop)されてメモリから解放される
  3. 呼び出し元の main 関数では、すでに s は破棄されており参照できない
dak2dak2

借用(borrow)

所有権をムーブさせずに参照だけさせたい場合に使う

#![allow(unused)]
fn main() {
    let s = String::from("hello");  // sがスコープに入る

    takes_ownership(&s);             // & により変数を貸し出す
    
    println!("let s: {}", s)                // s は借用させただけなので所有権を保持していることから参照できる
}
 
fn takes_ownership(some_string: &String) { // some_stringがスコープに入る。
    println!("some_string: {}", some_string); // some_string の所有権は持っていないのでスコープ外となっても drop されない
} 
  1. takes_ownership 関数に s を貸し出す(& operator)
  2. takes_ownership 関数では s の所有権は無く借用しているだけなので、関数のスコープを抜けても drop されない
  3. 呼び出し元の main 関数は s の所有権を持っているので、takes_ownership を抜けたあとでも println!s を参照できる
    • s のライフタイムは main 関数のスコープ内になっている

借用を使うということは、まだそのスコープ内で変数を使う可能性があるということを示してるとも言えそう

dak2dak2

参照外し(dereference)

参照されたコードから値を取得する
参照は実質的にポインタなので、ポインタの指す値を取得すること

fn main() {
    let a = 1;

    let b = &a;

    println!("{}", *b);
}

=> 1

println に渡すケースだと、* つけなくても値が出力されるから、参照を渡しても内部で dereference されてそうな気がする

fn main() {
    let a = 1;

    let b = &a;

    println!("{}", b);
}
dak2dak2

Copy トレイトを実装していない型だと dereference がエラーになるよう
下記の例だと、String は Copy トレイトを実装していないが、usize は Copy トレイトを実装している
Copy を実装している型は dereference 時に move ではなく copy になるためエラーにならない。
Copy を実装していない String は dereference 時に所有権を move しようとしているとみなしてエラー
だから、error[E0507]: cannot move out of *b which is behind a shared reference と怒られる

fn two() {
    let a = String::new();

    // reference to a is created. a can still be used, though exactly how it can be used depends on context
    let b = &a;

    // dereferencing can't move out of a, we can read the value but not move it
    *b; // error[E0507]: cannot move out of `*b` which is behind a shared reference
}

fn three() {
    let a = 0usize;

    // reference to a is created. a can still be used, though exactly how it can be used depends on context
    let b = &a;

    // dereferencing is allowed because the type of a implements Copy, which means its impossible to move out of a. Attempts to move out of a will always result in a new copy of the value
    *b; // Creates a new usize value copied from a.
}

https://users.rust-lang.org/t/dereferencing-trying-to-get-a-handle-on-it/89618/7

dak2dak2

6章の uniq まで実装してみた
あとは大体同じような実装だったのでここら辺で終わる

このスクラップは2ヶ月前にクローズされました
ログインするとコメントできます