Skip to content

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.

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` trait
7 | greet(name); // name moves into greet here
| ---- value moved here
8 | 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 value
help: 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;).

Read the three labels from top to bottom. They tell the whole story:

  1. Line 6: name is a String, and String is not Copy. So passing it somewhere moves it.
  2. Line 7: greet(name) moves the string into the function. greet now owns it, and the string is dropped when greet ends.
  3. Line 8: you try to read name again, but main doesn’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.

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.

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!
▶ Try it Yourself

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

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 points
Original name: Ferris
▶ Try it Yourself

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

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 }
▶ Try it Yourself

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.

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,
-- Ferris
▶ Try it Yourself

This works well for “builder” style functions. For a simple in-place change, fn add_signature(letter: &mut String) is often easier to read.

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.

  • 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 String where &str is expected after switching to a borrow.
  • All compiler errors: the full list of error pages.

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