How to fix error E0505: cannot move out of a value because it is borrowed
Short answer: a reference to a value was still in use when the value itself was moved. A move hands ownership to someone else, which would leave the reference pointing at a value that no longer belongs here. Finish the read before the move, or clone what the reference needs so no borrow is held across the move.
The error
Section titled “The error”fn main() { let config = String::from("debug"); let report = &config; let final_config = config; println!("report says {report}"); println!("config is now {final_config}");}Compiling that with cargo build prints:
error[E0505]: cannot move out of `config` because it is borrowed --> src/main.rs:4:24 |2 | let config = String::from("debug"); | ------ binding `config` declared here3 | let report = &config; | ------- borrow of `config` occurs here4 | let final_config = config; | ^^^^^^ move out of `config` occurs here5 | println!("report says {report}"); | ------ borrow later used here |help: consider cloning the value if the performance cost is acceptable |3 | let report = &config.clone(); | ++++++++
For more information about this error, try `rustc --explain E0505`.report borrows config, and because report is still used after let final_config = config;,
the compiler treats that borrow as alive when the move happens. A borrowed value cannot be moved
away — Rust rejects the combination.
Why Rust stops this
Section titled “Why Rust stops this”A move is how ownership changes hands. After let final_config = config;, the original config
binding no longer owns anything — the string now belongs to final_config. But report is a
reference into the memory the original binding owned, and it is still expected to read that memory
on the next line. If the move went through, report would point at storage whose owner has already
changed hands.
String does not implement Copy, so let final_config = config; is a transfer, not a duplicate.
Rust’s rule against moving a value while a borrow of it is alive is what turns that dangling
reference into a compile error instead of a crash or garbage values.
The message says because it is borrowed because the borrow is the thing already there: the read was still going, and the move walked out from under it.
“Borrowed” means “still in use”
Section titled ““Borrowed” means “still in use””Like E0502, this error is about borrows that are alive at the time of the move, not about how many
& expressions the file contains. A borrow ends at its last use, so finishing the read before the
move is enough — no extra scope needed:
fn main() { let config = String::from("debug"); let report = &config; println!("report says {report}"); let final_config = config; println!("config is now {final_config}");}This prints:
report says debugconfig is now debugreport’s borrow ends at the println! line. By the time the move happens, there is no borrow left
to conflict with, so the same two operations in a different order compile fine.
The fixes
Section titled “The fixes”Finish the read first — shown above. Reorder so every use of the reference happens before the move.
Clone what the borrow reads. If the read only needs the value, take an owned copy. The clone holds no borrow, so it can outlive the move:
fn main() { let config = String::from("debug"); let report = config.clone(); let final_config = config; println!("report says {report}, config is now {final_config}");}This prints report says debug, config is now debug. For small types like i32, indexing and
field access already copy, so this error can only happen with types that move: String, Vec,
HashMap, and the rest.
The shape to recognise: passing a value to a function while a borrow is alive
Section titled “The shape to recognise: passing a value to a function while a borrow is alive”The most common real-world version is handing ownership to a function — a Vec<String> passed by
value, a String fed into std::mem::drop — while a reference into it is still in scope:
fn archive(logs: Vec<String>) { println!("archived {} entries", logs.len());}
fn main() { let logs = vec![String::from("boot ok"), String::from("user login")]; let latest = &logs[0]; archive(logs); println!("latest was {latest}");}The call archive(logs) moves the whole vector into the function, and latest still points into
it:
error[E0505]: cannot move out of `logs` because it is borrowed --> src/main.rs:8:13 |6 | let logs = vec![String::from("boot ok"), String::from("user login")]; | ---- binding `logs` declared here7 | let latest = &logs[0]; | ---- borrow of `logs` occurs here8 | archive(logs); | ^^^^ move out of `logs` occurs here9 | println!("latest was {latest}"); | ------ borrow later used here |help: consider cloning the value if the performance cost is acceptable |7 | let latest = &logs.clone()[0]; | ++++++++
For more information about this error, try `rustc --explain E0505`.Two ways out, depending on who owns the function:
fn archive(logs: Vec<String>) { println!("archived {} entries", logs.len());}
fn main() { let logs = vec![String::from("boot ok"), String::from("user login")]; let latest = logs[0].clone(); archive(logs); println!("latest was {latest}");}This prints:
archived 2 entrieslatest was boot okThe clone takes its own copy before the move, so latest no longer borrows the vector. If
archive is yours to change, the better fix is to borrow at the call site instead: change the
signature to fn archive(logs: &[String]) and call archive(&logs). Then nothing moves, and the
error disappears without any copying.
Not this error
Section titled “Not this error”Three neighbours that look similar but carry different codes:
- Mutating (not moving) the value while a shared borrow is alive is E0502 (
cannot borrow as mutable ...). There the second operation is a write through&mut; here it is a move. - Using the value after the move with no borrow involved is E0382 (
borrow of moved value) — the value was already gone and something tried to read the original binding again. - Trying to move out through a reference —
let s = *latest;on a&String— is E0507 (cannot move out of borrowed content). There the move fails even with nothing else in scope, because a reference never gives up what it points at.
Related errors
Section titled “Related errors”- E0502 — cannot borrow as mutable while it is borrowed as immutable
- E0382 — borrow of moved value
E0507— cannot move out of borrowed content