Rust Error E0382: Borrow of Moved Value
E0382 means you gave a value away (you moved it) and then tried to use it again. It is the most common error people meet in their first week of Rust, and it almost always has a one-line fix. This page shows the fix first, then explains what is going on. For the other errors in this series, see Rust compiler errors.
The error message
Section titled “The error message”error[E0382]: borrow of moved value: `name` --> src/main.rs:8:25 |6 | let name = String::from("Ferris"); | ---- move occurs because `name` has type `String`, which does not implement the `Copy` trait7 | greet(name); // name moves into greet here | ---- value moved here8 | println!("Goodbye, {name}!"); // ❌ name is gone | ^^^^ value borrowed here after move |note: consider changing this parameter type in function `greet` to borrow instead if owning the value isn't necessary --> src/main.rs:1:16 |1 | fn greet(name: String) { | ----- ^^^^^^ this parameter takes ownership of the valuehelp: consider cloning the value if the performance cost is acceptable |7 | greet(name.clone()); // name moves into greet here | ++++++++This is the program that produces it:
fn greet(name: String) { println!("Hello, {name}!");}
fn main() { let name = String::from("Ferris"); greet(name); // name moves into greet here println!("Goodbye, {name}!"); // ❌ name is gone}You may also see the wording “use of moved value”. It is the same error. Rust says “borrow” when the later line only reads the value (like println!) and “use” when it tries to move it again (like let other = name;).
What it means
Section titled “What it means”Read the three labels from top to bottom. They tell the whole story:
- Line 6:
nameis aString, andStringis notCopy. So passing it somewhere moves it. - Line 7:
greet(name)moves the string into the function.greetnow owns it, and the string is dropped whengreetends. - Line 8: you try to read
nameagain, butmaindoesn’t own it anymore.
Think of handing your only house key to a friend. Once you’ve handed it over, you can’t open the door with it, even though you remember that you used to have it.
Why Rust does this
Section titled “Why Rust does this”Every value in Rust has exactly one owner. When the owner goes away, the value is cleaned up. This is how Rust frees memory without a garbage collector. The rules are explained on the Ownership page.
If two variables both owned the same String, both would try to free the same memory at the end. That is a “double free”, a real bug that crashes C and C++ programs. Rust prevents it by making assignment and function calls move the value, so there is always just one owner.
Small, simple types like i32, bool, and char are Copy: passing them makes a cheap copy, and you never see E0382 for them. Types that own heap memory, like String, Vec, and most structs, move instead.
How to fix it
Section titled “How to fix it”Fix 1: Borrow with &
Section titled “Fix 1: Borrow with &”Choose this when the function only needs to look at the value. This is the right fix most of the time. Change the parameter to a reference and pass &name:
fn greet(name: &str) { println!("Hello, {name}!");}
fn main() { let name = String::from("Ferris"); greet(&name); // lend it, don't give it away println!("Goodbye, {name}!"); // ✅ still ours}Output:
Hello, Ferris!Goodbye, Ferris!&String would also work, but &str is the usual choice because it accepts string literals too. See Borrowing and Strings. If the function needs to change the value, use &mut instead.
Fix 2: Clone it
Section titled “Fix 2: Clone it”Choose this when the other code really needs its own copy, for example to store it in a struct or a Vec, and you still need the original. .clone() makes a full, independent copy:
struct Player { name: String, score: u32,}
fn main() { let name = String::from("Ferris"); let player = Player { name: name.clone(), score: 0 }; println!("{} starts with {} points", player.name, player.score); println!("Original name: {name}");}Output:
Ferris starts with 0 pointsOriginal name: FerrisBe honest with yourself here: cloning a short string or a small Vec is cheap, and it’s completely fine, especially while you are learning. Only worry about it when you clone large data inside a hot loop. If a borrow (Fix 1) works, prefer that.
Fix 3: Derive Copy for small types
Section titled “Fix 3: Derive Copy for small types”Choose this for small structs made only of simple values, like coordinates, colors, or IDs. Add #[derive(Clone, Copy)] and the struct gets copied instead of moved:
#[derive(Debug, Clone, Copy)]struct Point { x: i32, y: i32,}
fn show(p: Point) { println!("({}, {})", p.x, p.y);}
fn main() { let start = Point { x: 0, y: 3 }; show(start); // copies start show(start); // ✅ works again println!("{:?}", start);}Output:
(0, 3)(0, 3)Point { x: 0, y: 3 }Copy only works if every field is Copy too. A struct with a String or Vec field can’t be Copy; use Clone and call .clone() for those. More on this in Common traits.
Fix 4: Give ownership back
Section titled “Fix 4: Give ownership back”Choose this when a function takes a value, changes it, and you want the result. The function returns the value, and you store it again:
fn add_signature(mut letter: String) -> String { letter.push_str("\n-- Ferris"); letter // hand it back}
fn main() { let letter = String::from("Dear Rustacean,"); let letter = add_signature(letter); // take it back println!("{letter}");}Output:
Dear Rustacean,-- FerrisThis works well for “builder” style functions. For a simple in-place change, fn add_signature(letter: &mut String) is often easier to read.
Where you’ll usually see it
Section titled “Where you’ll usually see it”A for loop over a Vec. for task in tasks moves the whole vector into the loop, so you can’t use tasks after it:
for task in tasks { println!("Doing: {task}");}println!("{} tasks done", tasks.len()); // ❌ `tasks` moved due to this implicit call to `.into_iter()`Fix: loop over a borrow, for task in &tasks. See for loops and Iterators.
Putting a value into a Vec or a struct. names.push(name) and Order { customer } both move the value inside. Using name or customer afterward fails. Fix: read it back from its new home (names[0], order.customer), do your other work before the move, or push a .clone().
Moving inside a loop. If log.push(message) sits inside a loop, the first pass moves message and the second pass has nothing left. The compiler says value moved here, in previous iteration of loop. Fix: push message.clone(), or create the value inside the loop so each pass has a fresh one.
Threads and move closures. thread::spawn(move || ...) moves every captured variable into the new thread. Fix: clone the data before spawning, or share it with Arc. See Concurrency and Shared state.
Related errors
Section titled “Related errors”- E0502: you borrowed a value and then tried to change it while the borrow was still in use.
- E0499: two mutable borrows of the same value at the same time.
- E0106: a missing lifetime specifier, often seen when you return a reference to avoid a move.
- E0308: mismatched types, for example passing
Stringwhere&stris expected after switching to a borrow. - All compiler errors: the full list of error pages.
Quick check
Section titled “Quick check”Quick check
1. You call save(report) where report is a String, then print report. Why do you get E0382?
2. A function only needs to read a Vec<String>. What is usually the best fix?
3. Which struct can use #[derive(Clone, Copy)]?
Score: 0 / 3
Read more
Section titled “Read more”- E0382 — Rust error codes index: the official explanation of this error, with more examples.
- What Is Ownership? — The Rust Book: the chapter that explains moves,
Clone, andCopystep by step. - References and Borrowing — The Rust Book: how
&lets you use a value without moving it. - Ownership and moves — Rust by Example: short runnable examples of moves and partial moves.
Copytrait — standard library docs: which types areCopy, and the difference betweenCopyandClone.