How to fix error E0507: cannot move out of borrowed content
Short answer: the code dereferenced a reference and tried to move the value behind it. A
dereference of a shared reference can only copy; a move would leave the value the reference points
at holding nothing. Clone the value, borrow deeper instead of moving, or — when a &mut is
available — swap the value out with std::mem::take.
The error
Section titled “The error”fn main() { let greeting = String::from("hello"); let r = &greeting; let owned = *r; println!("owned is {owned}"); println!("greeting is still {greeting}");}Compiling that with cargo build prints:
error[E0507]: cannot move out of `*r` which is behind a shared reference --> src/main.rs:4:17 |4 | let owned = *r; | ^^ move occurs because `*r` has type `String`, which does not implement the `Copy` trait |help: consider removing the dereference here |4 - let owned = *r;4 + let owned = r; |help: consider cloning the value if the performance cost is acceptable |4 - let owned = *r;4 + let owned = r.clone(); |
For more information about this error, try `rustc --explain E0507`.r is a &String. Writing *r means “the String that r points at”, and let owned = ...
means the String should move into the new binding. But r only points at that String — it
does not own it. Moving the value out from behind the reference would leave the original with
nothing in it, and the compiler refuses.
Why Rust stops this
Section titled “Why Rust stops this”The same dereference compiles fine when the type is Copy:
fn main() { let answer = 42; let r = &answer; let n = *r; println!("n is {n}, answer is still {answer}");}This prints:
n is 42, answer is still 42i32 implements Copy, so *r copies the four bytes and both variables keep a valid value. A
String owns its heap buffer, so *r cannot be a copy — it can only be a move, and a move through
a shared reference would tear a hole in a value that somebody else still owns. That is the whole
error: a reference never gives up what it points at.
Note that the reference being mutable does not change this. A &mut String lets you write
*r = String::from("new") — replace the value in place — but let owned = *r; on a &mut String
is still E0507, because afterwards the original would hold nothing at all.
The fixes
Section titled “The fixes”Clone it. When the code needs an owned value, say so:
fn main() { let greeting = String::from("hello"); let r = &greeting; let owned = r.clone(); println!("owned is {owned}"); println!("greeting is still {greeting}");}This prints:
owned is hellogreeting is still helloThe clone is a new allocation that this binding owns outright — no reference, no move, no conflict.
Borrow deeper instead of moving. Most of the time the value is only being read, and a move was never needed. Take a reference (or a slice) and the error disappears because nothing moves:
fn main() { let greeting = String::from("hello"); let r = &greeting; let slice: &str = r; println!("slice is {slice}"); println!("greeting is still {greeting}");}This prints:
slice is hellogreeting is still hello&String coerces to &str, so slice reads the same bytes without owning anything. If the
original code had a type annotation or a function argument demanding String, changing that
demand to &str is usually the better fix than cloning.
Take it and leave something behind. When there is a mutable reference and the value genuinely has to
come out, put something harmless in its place. std::mem::take swaps the value out for
Default::default() — an empty String, a 0, a None — through a &mut:
struct Person { name: String, city: String,}
fn extract_name(p: &mut Person) -> String { std::mem::take(&mut p.name)}
fn main() { let mut p = Person { name: String::from("ada"), city: String::from("oslo"), }; let name = extract_name(&mut p); println!("took {name}, the field is now {:?}", p.name);}This prints:
took ada, the field is now ""The struct keeps a valid (empty) String in the field, and the caller gets the original. For
Option fields the same job has a dedicated method: p.nickname.take() pulls the Some value out
and leaves None behind.
The shape to recognise: indexing a Vec<String>
Section titled “The shape to recognise: indexing a Vec<String>”The most common real-world version is a plain index into a vector of non-Copy values. Indexing
looks like reading, but logs[0] evaluates to the String itself — by value:
fn main() { let logs = vec![String::from("boot ok"), String::from("user login")]; let first = logs[0]; println!("first is {first}");}error[E0507]: cannot move out of index of `Vec<String>` --> src/main.rs:3:17 |3 | let first = logs[0]; | ^^^^^^^ move occurs because value has type `String`, which does not implement the `Copy` trait |help: consider borrowing here |3 | let first = &logs[0]; | +help: consider cloning the value if the performance cost is acceptable |3 | let first = logs[0].clone(); | ++++++++
For more information about this error, try `rustc --explain E0507`.The two helpers offer exactly the two fixes from above. Borrow when the element only needs reading:
fn main() { let logs = vec![String::from("boot ok"), String::from("user login")]; let first = &logs[0]; println!("first is {first}"); println!("{} entries kept", logs.len());}This prints:
first is boot ok2 entries kept&logs[0] borrows the element, so the vector stays intact and logs.len() still works afterwards.
Clone when the code needs the element to outlive the vector.
The same error on a struct field
Section titled “The same error on a struct field”Field access through a reference behaves the same way: p.name on a &Person is a read of a
String, but binding it to a new name turns that read into a move:
struct Person { name: String, city: String,}
fn loudest(p: &Person) { let name = p.name; println!("{name}!!");}
fn main() { let p = Person { name: String::from("ada"), city: String::from("oslo") }; loudest(&p); println!("after: {} from {}", p.name, p.city);}error[E0507]: cannot move out of `p.name` which is behind a shared reference --> src/main.rs:7:16 |7 | let name = p.name; | ^^^^^^ move occurs because `p.name` has type `String`, which does not implement the `Copy` trait |help: consider borrowing here |7 | let name = &p.name; | +help: consider cloning the value if the performance cost is acceptable |7 | let name = p.name.clone(); | ++++++++
For more information about this error, try `rustc --explain E0507`.The fix is the borrow-deeper one: let name = &p.name;, or pass &p.name straight to whatever
needed it. Nothing moves, and p keeps every field.
Not this error
Section titled “Not this error”Three neighbours that look similar but carry different codes:
- Moving the value itself while a reference to it is still in use is E0505 (
cannot move out of a value because it is borrowed). There the value moves directly; here the move goes through a reference, which fails even with nothing else in scope. - Using a value after it was already moved, with no reference involved, is E0382 (
borrow of moved value). - Getting a
&mutwhere only&was declared — or nomuton the binding — is E0596 (cannot borrow as mutable), a separate error about mutability rather than moves.