Rust 所有権クイズ!

に公開

はじめに

こんにちは、ファスト株式会社のYTです!

突然ですが、Rustクイズです!
以下のコードのうち、ターミナルにこんにちは!と出力されるのはどれでしょうか?

1: Some(greet)

fn main() {
    let greet = Some(String::from("こんにちは!"));

    match greet {
        Some(greet) => println!("あいさつをします!"),
        None => println!("あいさつしません!"),
    };

    println!("{}", greet.unwrap());
}

2: Some(_greet)

fn main() {
    let greet = Some(String::from("こんにちは!"));

    match greet {
        Some(_greet) => println!("あいさつをします!"),
        None => println!("あいさつしません!"),
    };

    println!("{}", greet.unwrap());
}

3: Some(_)

fn main() {
    let greet = Some(String::from("こんにちは!"));

    match greet {
        Some(_) => println!("あいさつをします!"),
        None => println!("あいさつしません!"),
    };

    println!("{}", greet.unwrap());
}

正解発表

正解は3でした!

各コードを実行した結果は以下のようになりました。

パターン1について

$ cargo run
   Compiling scrap v0.1.0
warning: unused variable: `greet`
 --> src/main.rs:5:14
  |
5 |         Some(greet) => println!("あいさつをします!"),
  |              ^^^^^ help: if this is intentional, prefix it with an underscore: `_greet`
  |
  = note: `#[warn(unused_variables)]` on by default

error[E0382]: use of partially moved value: `greet`
 --> src/main.rs:9:22
  |
5 |         Some(greet) => println!("あいさつをします!"),
  |              ----- value partially moved here
...
9 |     println!("{}", greet.unwrap());
  |                    ^^^^^ value used here after partial move
  |
  = note: partial move occurs because value has type `String`, which does not implement the `Copy` trait
help: borrow this binding in the pattern to avoid moving the value
  |
5 |         Some(ref greet) => println!("あいさつをします!"),
  |              +++

For more information about this error, try `rustc --explain E0382`.
warning: `scrap` (bin "scrap") generated 1 warning
error: could not compile `scrap` (bin "scrap") due to 1 previous error; 1 warning emitted

所有権が移動した後に変数greetを使用しようとするため、エラーになってしまいました。
Rustコンパイラは所有権を移動させない方法として、refを付けることを提案してくれていますね!
また、greetという変数を使用していないという警告が出ていますね!

パターン2について

 cargo run
   Compiling scrap v0.1.0
error[E0382]: use of partially moved value: `greet`
 --> src/main.rs:9:22
  |
5 |         Some(_greet) => println!("あいさつをします!"),
  |              ------ value partially moved here
...
9 |     println!("{}", greet.unwrap());
  |                    ^^^^^ value used here after partial move
  |
  = note: partial move occurs because value has type `String`, which does not implement the `Copy` trait
help: borrow this binding in the pattern to avoid moving the value
  |
5 |         Some(ref _greet) => println!("あいさつをします!"),
  |              +++

For more information about this error, try `rustc --explain E0382`.
error: could not compile `scrap` (bin "scrap") due to 1 previous error

こちらもまた、所有権が移動した後に変数greetを使用しようとするため、エラーになってしまいました。
しかし、今回はgreetを使用していないという警告は出ていませんね!

パターン3について

$ cargo run
   Compiling scrap v0.1.0
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.41s
     Running `target/debug/scrap`
あいさつをします!
こんにちは!

正常にターミナルに出力することができました!
左辺を_とすると、所有権の移動が発生しないんですね!

まとめ

  • 変数として束縛すると所有権が移動する(パターン1・2)
  • _は値を無視するパターンなので所有権も移動しない(パターン3)
  • 未使用変数を警告なしで保持したい場合は _variable のように接頭辞_を付ける

参考文献

https://doc.rust-jp.rs/rust-by-example-ja/variable_bindings.html

https://doc.rust-jp.rs/book-ja/ch18-03-pattern-syntax.html#名前を_で始めて未使用の変数を無視する

FAST Tech Blog

Discussion