web-dev-qa-db-ja.com

ラップを解除するときに、共有参照の背後にある値から移動することはできません

これは私が実行しようとしているコードです:

fn my_fn(arg1: &Option<Box<i32>>) -> i32 {
    if arg1.is_none() {
        return 0;
    }
    let integer = arg1.unwrap();
    *integer
}

fn main() {
    let integer = 42;
    my_fn(&Some(Box::new(integer)));
}

Rust遊び場

以前のバージョンのRustで次のエラーが発生します。

error[E0507]: cannot move out of borrowed content
 --> src/main.rs:5:19
  |
5 |     let integer = arg1.unwrap();
  |                   ^^^^ cannot move out of borrowed content

そして、より現代的なバージョンでは:

error[E0507]: cannot move out of `*arg1` which is behind a shared reference
 --> src/main.rs:5:19
  |
5 |     let integer = arg1.unwrap();
  |                   ^^^^
  |                   |
  |                   move occurs because `*arg1` has type `std::option::Option<std::boxed::Box<i32>>`, which does not implement the `Copy` trait
  |                   help: consider borrowing the `Option`'s content: `arg1.as_ref()`

ボローチェッカーの問題に関するドキュメントはすでにたくさんあるようですが、それを読んだ後でも、問題を理解することはできません。

なぜこれがエラーなのですか、どうすれば解決できますか?

14
Moebius

Option::unwrap() オプションを消費します。つまり、値でオプションを受け入れます。ただし、値はなく、参照のみがあります。それがエラーの原因です。

あなたのコードは慣用的に次のように書かれるべきです:

fn my_fn(arg1: &Option<Box<i32>>) -> i32 {
    match arg1 {
        Some(b) => **b,
        None => 0,
    }
}

fn main() {
    let integer = 42;
    my_fn(&Some(Box::new(integer)));
}

Rust遊び場

または、 Option::as_ref または Option::as_mut と組み合わせて Option::map_or のようなOptionコンビネータを使用できます。 =、Shepmasterが提案したように:

fn my_fn(arg1: &Option<Box<i32>>) -> i32 {
    arg1.as_ref().map_or(0, |n| **n)
}

このコードは、i32が自動的にコピー可能であるという事実を使用しています。 Box内の型がCopyでない場合、値ごとに内部値を取得することはできません。クローンを作成するか、を返すことしかできません。たとえば、次のような参照:

fn my_fn2(arg1: &Option<Box<i32>>) -> &i32 {
    arg1.as_ref().map_or(&0, |n| n)
}

オプションへの不変の参照しかないため、その内容への不変の参照のみを返すことができます。 Rustは、リテラル0を静的な値に昇格させて、入力値がない場合にそれを返すことができるようにするのに十分賢いです。

16