How to fix error E0382: borrow of moved value
Short answer: the value was moved somewhere else, and you are trying to use the old variable. Pass
a reference (&value) instead of the value, or clone() it if you genuinely need two copies.
The error
Section titled “The error”fn main() { let s1 = String::from("hello"); let s2 = s1; // ownership moves to s2
println!("{}", s1); // E0382}Compiling that with cargo build prints:
error[E0382]: borrow of moved value: `s1` --> src/main.rs:4:20 |2 | let s1 = String::from("hello"); | -- move occurs because `s1` has type `String`, which does not implement the `Copy` trait3 | let s2 = s1; | -- value moved here4 | println!("{}", s1); | ^^ value borrowed here after move |help: consider cloning the value if the performance cost is acceptable |3 | let s2 = s1.clone(); | ++++++++Read it from the bottom up. The compiler tells you three things: where the value moved, why the type moves, and what to write instead.
Why it happens
Section titled “Why it happens”The compiler’s own note is the whole explanation: String does not implement Copy. Types that
manage heap memory transfer ownership rather than duplicating it, so s1 stops being a valid name
for anything the moment s2 = s1 runs.
If you are not sure why this rule exists at all, read Why does Rust move values instead of copying them? first. The short version: two owners of one heap buffer would free it twice.
Fix 1 — borrow instead of move (prefer this)
Section titled “Fix 1 — borrow instead of move (prefer this)”Most moves are unnecessary. The function only needed to read the value, so pass a reference:
fn print_it(s: &String) { println!("{s}");}
fn main() { let s1 = String::from("hello"); print_it(&s1); println!("{s1}"); // still valid — nothing moved}Change the signature to &String (or better, &str) and the call site to &s1:
fn print_it(s: &str) { // accepts &String too, via deref coercion println!("{s}");}
fn main() { let s1 = String::from("hello"); print_it(&s1); print_it("a string literal works too");}This costs nothing at runtime and is the fix the compiler is nudging you towards.
Fix 2 — clone, when you really need two owners
Section titled “Fix 2 — clone, when you really need two owners”fn main() { let s1 = String::from("hello"); let s2 = s1.clone();
println!("{s1} {s2}");}clone() allocates and copies the whole heap buffer. It is the right answer when you actually want
two independent values — and the wrong answer when you are using it to silence the compiler. If you
find clone() on every second line, the data flow needs rethinking, not more copying.
Fix 3 — return the value from the function
Section titled “Fix 3 — return the value from the function”Taking ownership can be fine if you hand it back:
fn consume_and_return(mut s: String) -> String { s.push_str(" world"); s}
fn main() { let s1 = String::from("hello"); let s1 = consume_and_return(s1); // moved out, moved back in println!("{s1}"); // hello world}Fix 4 — shared ownership with Rc, if one owner is not enough
Section titled “Fix 4 — shared ownership with Rc, if one owner is not enough”When several parts of a program genuinely need to own the same data, wrap it:
use std::rc::Rc;
fn main() { let shared = Rc::new(String::from("hello"));
let a = Rc::clone(&shared); let b = Rc::clone(&shared);
println!("{shared} {a} {b}");}Rc counts references and only drops the value when the last one goes away. Use Arc instead if the
values cross thread boundaries — and remember that this is a design decision with a runtime cost, not
a workaround for a borrow error.
Which fix should you choose?
Section titled “Which fix should you choose?”| Situation | Fix |
|---|---|
| The callee only reads the value | Pass &value |
| The callee only mutates in place | Pass &mut value |
| You truly need two independent copies | clone() |
| A function takes ownership and you want it back | Return the value |
| Multiple long-lived owners are genuinely required | Rc / Arc |
Common places E0382 shows up
Section titled “Common places E0382 shows up”Inside a loop — iterating by value moves the collection on the first pass:
fn main() { let v = vec![1, 2, 3]; for x in &v { // &v, not v println!("{x}"); } println!("{v:?}"); // still usable}Inside a closure — the compiler message mentions FnOnce:
error[E0382]: use of moved value: `s`note: closure cannot be invoked more than once because it moves the variable `s` out of its environmentFix it by borrowing inside the closure (|| println!("{s}") instead of || drop(s)), or by using
move deliberately when the closure should own the value.
Returning a field out of a struct — you cannot move one field out of a borrowed struct. Clone the field, or restructure so the function takes ownership.
Related errors
Section titled “Related errors”E0505— cannot move out of a value while it is borrowedE0507— cannot move out of borrowed contentE0597— borrowed value does not live long enough