Code Fix

上級

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  }
error[E0507]: cannot move out of `wref.value` which is behind a shared reference

wrefは借用しているだけなので、その中のフィールドの所有権を勝手に持ち出すことはできません。

直し方: wref.valuewref.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  }
error[E0507]: cannot move out of `href.text` which is behind a shared reference

共有参照の向こうにあるフィールドをムーブすると、参照元の構造体が不完全な状態になるため禁止されています。

直し方: href.texthref.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  }
error[E0507]: cannot move out of `bref.content` which is behind a shared reference

フィールドの値を借用元の外に持ち出したい場合は、cloneで複製してから渡す必要があります。

直し方: bref.contentbref.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  }
error[E0507]: cannot move out of `rref.label` which is behind a shared reference

参照経由でもフィールドを読むことはできますが、所有権ごと取り出す(ムーブする)ことはできません。

直し方: rref.labelrref.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  }
error[E0507]: cannot move out of `eref.name` which is behind a shared reference

E0507はこのように「借りているだけの構造体からフィールドを持ち出そうとした」ときに検出されます。

直し方: eref.nameeref.name.clone() にします。

この問題を解いてみる →

よくある誤解

「参照経由でも、フィールドの値自体はいつでも取り出して使えるはず」という思い込みは誤りです。共有参照の向こうにある値の所有権を奪うと、参照元の構造体が不完全な状態になってしまうため、Rustはこれを禁止します。

まとめ

E0507: cannot move out of ... which is behind a shared referenceは上級でつまずきやすい項目です。上の5パターンを実際に手で直すと、エラーメッセージのどこを読めばよいかが掴めます。

演習をはじめる

関連するエラー

広告
広告スロット(未設定)