E0382: borrow of moved value(ムーブ後の値の使用)の原因と直し方
StringやVecのようにCopyトレイトを実装しない値を別の変数に代入すると、所有権が移動(ムーブ)します。ムーブ後に元の変数を使おうとすると、コンパイル時にE0382エラーになります。
エラーメッセージの読み方
error[E0382]: borrow of moved value: `s1`
E0382- エラーコード — `rustc --explain E0382` で詳しい説明が見られます
borrow of moved value: `s1`- 詳細メッセージ — 何が問題だったか
このエラーが出る典型パターン
パターン1
1 fn main() { 2 let s1 = String::from("hello"); 3 let s2 = s1 ; ^ 4 println!("{} {}", s1, s2); 5 }
s1をそのままs2に代入すると所有権が移動し、s1は無効になります。両方使いたい場合はcloneで複製します。
直し方: s1 を s1.clone() にします。
パターン2
1 fn main() { 2 let name1 = String::from("taro"); 3 let name2 = name1 ; ^ 4 println!("{} {}", name1, name2); 5 }
Stringのようにヒープにデータを持つ型は、代入すると値がコピーされず所有権だけが移動します。
直し方: name1 を name1.clone() にします。
パターン3
1 fn main() { 2 let title = String::from("report"); 3 let copy = title ; ^ 4 println!("{} {}", title, copy); 5 }
整数のようなCopy型と違い、Stringは代入のたびに所有権の移動が起こります。
直し方: title を title.clone() にします。
パターン4
1 fn main() { 2 let label = String::from("final"); 3 let backup = label ; ^ 4 println!("{} {}", label, backup); 5 }
ムーブ後に元の変数を使おうとすると、コンパイラがその場でE0382エラーとして検出します。
直し方: label を label.clone() にします。
パターン5
1 fn main() { 2 let word = String::from("guide"); 3 let alt = word ; ^ 4 println!("{} {}", word, alt); 5 }
両方の変数を独立して使いたい場合は、複製のコストと引き換えにcloneを使う必要があります。
直し方: word を word.clone() にします。
よくある誤解
「代入は値のコピーだから、元の変数もそのまま使えるはず」という思い込みは誤りです。Copyを実装しない型の代入は所有権の移動であり、元の変数はその時点で無効になります。
実務での勘所
この制約は、CやC++で問題になるdouble free(同じメモリを2回解放してしまうバグ)を、安全なRustのコードでは構造的に起こりえなくするための仕組みです。もし代入後も両方の変数が同じヒープ上のデータの所有者だと思い込んでいたら、両方がスコープを抜けるときにそれぞれdropが呼ばれ、同じメモリを2回解放しようとしてしまいます。代入のたびに所有権を移動させ、元の変数を無効化することで、「そのデータの持ち主は常にちょうど1つ」という状態を型システムで保証し、二重解放が原理的に起きないようにしています。