できない.dev

Rust で「borrow of moved value」(E0382) が解決できない

所有権が別の変数や関数へ移った後に元の変数を使うと E0382 になる。
参照 & を渡す、clone() で複製する、Copy を実装するのいずれかで解決する。
ループ内での move も同じ理由で起きる。

公開:

要約

borrow of moved value(E0382)は、所有権が移った後の変数を使ったときに出る。
Rust では Rc のような仕組みを使わない限り、1 つの値を 2 つの変数が同時に所有することはできない。

let s1 = String::from("hello");
let s2 = s1;          // ここで所有権が s2 へ移る
println!("{}", s1);   // error[E0382]: borrow of moved value: `s1`

対処は三択である。
所有権を渡さない(参照)、別の値を作る(clone)、そもそも move させない(Copy)。
用途に合うものを選ぶ。

よくある原因

  1. 代入で move している: StringVec のように Copy を持たない型は、代入した時点で元の変数が使えなくなる。
  2. 関数へ値渡ししている: 引数の型が String なら所有権ごと渡る。
    呼び出し後に元の変数を使えば同じエラーになる。
  3. for が所有権ごと奪っている: for x in vv を消費する。
    ループ後に v.len() を呼ぶと E0382 になる。
  4. クロージャの move: thread::spawn などへ渡すクロージャは値を取り込むため、外側では使えなくなる。

解決策

1. 参照を渡す

fn calculate_length(s: &String) -> usize {
    s.len()
}
 
let s1 = String::from("hello");
let len = calculate_length(&s1);
println!("{} {}", s1, len);   // s1 は所有権を保ったまま

読み取るだけなら参照で足りる。所有権の章(新しいタブで開く)が扱う基本形である。

2. clone() で複製する

let mut s1 = String::from("many");
let s2 = s1.clone();
s1.remove(0);
println!("{} {}", s1, s2);   // any many

複製のコストを払う代わりに、以降は互いに独立した値として扱える。

3. 軽い自作型は Copy にする

#[derive(Copy, Clone)]
struct Point { x: i32, y: i32 }
 
let mut p1 = Point { x: -1, y: 2 };
let p2 = p1;   // move ではなくコピー
p1.x = 1;

全フィールドが Copy である型に限って付けられる。String を含む型には付けられない。

4. コレクションは参照で反復する

let v = vec![1, 2, 3];
for x in &v {
    println!("{}", x);
}
println!("{}", v.len());   // v はまだ生きている

書き換えたいなら &mut v を使う。
所有権ごと消費したいときだけ for x in v を選ぶ。

この記事は役立ちましたか?