Skip to content
Select theme

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.

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.

The same dereference compiles fine when the type is Copy:

This prints:

n is 42, answer is still 42

i32 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.

Clone it. When the code needs an owned value, say so:

This prints:

owned is hello
greeting is still hello

The 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:

This prints:

slice is hello
greeting 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:

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:

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:

This prints:

first is boot ok
2 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.

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:

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.

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 &mut where only & was declared — or no mut on the binding — is E0596 (cannot borrow as mutable), a separate error about mutability rather than moves.