E0507: cannot move out of ... which is behind a shared reference
&で借りている構造体のフィールドを、所有権を要求する関数にそのまま渡そうとするとE0507エラーになります。参照の向こう側にある値は借りているだけなので、勝手に持ち出す(ムーブする)ことはできません。
エラーメッセージの読み方
error[E0507]: cannot move out of `wref.value` which is behind a shared reference
E0507- エラーコード — `rustc --explain E0507` で詳しい説明が見られます
cannot move out of `wref.value` which is behind a shared reference- 詳細メッセージ — 何が問題だったか
このエラーが出る典型パターン
パターン1
1 struct Wrapper { value: String } 2 fn take(s: String) -> usize { s.len() } 3 fn main() { 4 let w = Wrapper { value: String::from("hi") }; 5 let wref = &w; 6 let len = take(wref.value ); ^ 7 println!("{}", len); 8 }
wrefは借用しているだけなので、その中のフィールドの所有権を勝手に持ち出すことはできません。
直し方: wref.value を wref.value.clone() にします。
パターン2
1 struct Holder { text: String } 2 fn take(s: String) -> usize { s.len() } 3 fn main() { 4 let h = Holder { text: String::from("hello") }; 5 let href = &h; 6 let len = take(href.text ); ^ 7 println!("{}", len); 8 }
共有参照の向こうにあるフィールドをムーブすると、参照元の構造体が不完全な状態になるため禁止されています。
直し方: href.text を href.text.clone() にします。
パターン3
1 struct Box3 { content: String } 2 fn take(s: String) -> usize { s.len() } 3 fn main() { 4 let b = Box3 { content: String::from("item") }; 5 let bref = &b; 6 let len = take(bref.content ); ^ 7 println!("{}", len); 8 }
フィールドの値を借用元の外に持ち出したい場合は、cloneで複製してから渡す必要があります。
直し方: bref.content を bref.content.clone() にします。
パターン4
1 struct Record { label: String } 2 fn take(s: String) -> usize { s.len() } 3 fn main() { 4 let r = Record { label: String::from("note") }; 5 let rref = &r; 6 let len = take(rref.label ); ^ 7 println!("{}", len); 8 }
参照経由でもフィールドを読むことはできますが、所有権ごと取り出す(ムーブする)ことはできません。
直し方: rref.label を rref.label.clone() にします。
パターン5
1 struct Entry { name: String } 2 fn take(s: String) -> usize { s.len() } 3 fn main() { 4 let e = Entry { name: String::from("item1") }; 5 let eref = &e; 6 let len = take(eref.name ); ^ 7 println!("{}", len); 8 }
E0507はこのように「借りているだけの構造体からフィールドを持ち出そうとした」ときに検出されます。
直し方: eref.name を eref.name.clone() にします。
よくある誤解
「参照経由でも、フィールドの値自体はいつでも取り出して使えるはず」という思い込みは誤りです。共有参照の向こうにある値の所有権を奪うと、参照元の構造体が不完全な状態になってしまうため、Rustはこれを禁止します。
まとめ
E0507: cannot move out of ... which is behind a shared referenceは上級でつまずきやすい項目です。上の5パターンを実際に手で直すと、エラーメッセージのどこを読めばよいかが掴めます。