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)。
用途に合うものを選ぶ。
よくある原因
- 代入で move している:
StringやVecのようにCopyを持たない型は、代入した時点で元の変数が使えなくなる。 - 関数へ値渡ししている: 引数の型が
Stringなら所有権ごと渡る。
呼び出し後に元の変数を使えば同じエラーになる。 forが所有権ごと奪っている:for x in vはvを消費する。
ループ後にv.len()を呼ぶと E0382 になる。- クロージャの
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 を選ぶ。