RustArena

Rust error E0507: cannot move out of borrowed content (behind a reference)

Fix Rust's E0507 cannot move out of index, behind a shared reference: borrow, clone, as_ref, take or mem::replace.

E0507 means you tried to move a value out of something you only borrowed. Older compilers said "cannot move out of borrowed content"; today's messages are more specific: "cannot move out of index of Vec<String>" or "cannot move out of self.name which is behind a shared reference".

Moving out would leave a hole: the owner (the vector, the struct) would still think it holds a valid String, and would drop it again later. So you can only look through a borrow, or replace what you take.

Case 1: taking an element out of a Vec by index

fn main() {
    let names = vec![String::from("Ferris"), String::from("Corro")];
    let first = names[0];
    println!("{first}");
}
error[E0507]: cannot move out of index of `Vec<String>`
 --> src/main.rs:3:17
  |
3 |     let first = names[0];
  |                 ^^^^^^^^ move occurs because value has type `String`, which does not implement the `Copy` trait
  |
help: consider borrowing here
  |
3 |     let first = &names[0];
  |                 +

Fixes, depending on what you want:

(With Vec<i32>, let x = v[0]; works fine: i32 is Copy, so it's copied rather than moved.)

Case 2: returning a field from a &self method

struct User {
    name: String,
}

impl User {
    fn name(&self) -> String {
        self.name
    }
}

fn main() {
    let u = User { name: String::from("Ferris") };
    println!("{}", u.name());
}
error[E0507]: cannot move out of `self.name` which is behind a shared reference

Usually the right getter returns a reference: fn name(&self) -> &str { &self.name }. Return self.name.clone() if the caller needs ownership, or write fn into_name(self) -> String to consume the whole struct.

Case 3: unwrap() on an Option field

self.result.unwrap() inside a &self method fails the same way, because unwrap takes the Option by value. rustc suggests the fixes:

Case 4: dereferencing a reference into an owned value

let owned: String = *r; where r: &String tries to move the String out from behind r. Use r.clone(), or keep it as a reference.

The general toolbox

You want to... Use
look at it &x, as_ref(), as_deref(), iter()
a copy .clone()
take it, leave a default std::mem::take(&mut x)
take it, leave something specific std::mem::replace(&mut x, new)
take it out of an Option opt.take()
take it out of a Vec remove, swap_remove, pop, drain
consume the whole container iterate with into_iter(), or a method taking self

Example

struct Job {
    name: String,
    result: Option<String>,
}

impl Job {
    fn name(&self) -> &str {
        &self.name
    }

    fn peek(&self) -> Option<&str> {
        self.result.as_deref()
    }

    fn take_result(&mut self) -> Option<String> {
        self.result.take()
    }
}

fn main() {
    let mut names = vec![String::from("Ferris"), String::from("Corro"), String::from("Gopher")];
    let first = &names[0];
    println!("borrowed {first}");
    let copy = names[1].clone();
    let taken = std::mem::take(&mut names[2]);
    println!("copy {copy}, taken {taken}, left behind {:?}", names[2]);
    let removed = names.remove(0);
    println!("removed {removed}, now {names:?}");

    let mut job = Job { name: String::from("backup"), result: Some(String::from("ok: 42 files")) };
    println!("{} peek {:?}", job.name(), job.peek());
    let out = job.take_result();
    println!("took {:?}, now {:?}", out, job.peek());
}

Output:

borrowed Ferris
copy Corro, taken Gopher, left behind ""
removed Ferris, now ["Corro", ""]
backup peek Some("ok: 42 files")
took Some("ok: 42 files"), now None

Practice it