Rust Error E0382 Borrow of Moved Value and Three Fixes
Rust error E0382 means a value was moved and then used again. Here are the five fixes in the order to try them, with the code for each.

error[E0382]: borrow of moved value is the first ownership error most Rust programmers hit, and it keeps coming back in new disguises for months. The message says you used a value after giving it away. The fix is one of three: borrow instead of moving, clone the value so both sides have one, or share ownership on purpose. Which one is right depends on what the code is trying to do, and the compiler's own suggestion is not always the right choice. This post reproduces the three situations that cause nine out of ten of these errors, with the compiler's real output on Rust 1.99, and shows the fix for each.
The most common cause: passing a value to a function that takes ownership
The shortest reproduction is a function that takes a String by value and a caller that uses the string afterward:
fn consume(s: String) -> usize {
s.len()
}
fn main() {
let name = String::from("ferris");
let n = consume(name);
println!("{n} bytes in {name}");
}error[E0382]: borrow of moved value: `name`
--> e0382a.rs:8:29
|
6 | let name = String::from("ferris");
| ---- move occurs because `name` has type `String`, which does not implement the `Copy` trait
7 | let n = consume(name);
| ---- value moved here
8 | println!("{n} bytes in {name}");
| ^^^^ value borrowed here after move
|
note: consider changing this parameter type in function `consume` to borrow instead if owning the value isn't necessary
--> e0382a.rs:1:15
|
1 | fn consume(s: String) -> usize {
| ------- ^^^^^^ this parameter takes ownership of the value
help: consider cloning the value if the performance cost is acceptable
|
7 | let n = consume(name.clone());
| ++++++++Read the message from the top. Line 6 says why a move is a problem: String does not implement Copy, so assigning or passing it transfers ownership instead of duplicating it. Line 7 marks the move: consume(name) handed the string to the function, and name no longer owns anything. Line 8 is the use after the move, where println! borrows name to print it.
The compiler offers two ways out, and they are in order of how often they are right. The note about the parameter is the better one in almost every case. consume only reads the length, so it should not own the string at all. Change the parameter to a borrowed slice and pass a reference:
fn byte_len(s: &str) -> usize {
s.len()
}
fn main() {
let name = String::from("ferris");
let n = byte_len(&name); // borrow instead of giving the String away
println!("{n} bytes in {name}");
}6 bytes in ferrisTaking &str rather than &String is the idiom, because a &String coerces to &str automatically and string literals work too. The second suggestion, name.clone(), also compiles, at the cost of copying the string's bytes into a new heap allocation for a function that never needed them. Use clone when the callee really must own its own copy, such as storing it in a struct, and borrow when it only reads.
A loop that consumes the collection
The second most common cause is a for loop over a collection by value:
fn main() {
let names = vec![String::from("a"), String::from("b")];
for name in names {
println!("{name}");
}
println!("{} names", names.len());
}error[E0382]: borrow of moved value: `names`
--> e0382b.rs:6:26
|
2 | let names = vec![String::from("a"), String::from("b")];
| ----- move occurs because `names` has type `Vec<String>`, which does not implement the `Copy` trait
3 | for name in names {
| ----- `names` moved due to this implicit call to `.into_iter()`
...
6 | println!("{} names", names.len());
| ^^^^^ value borrowed here after move
|
help: consider iterating over a slice of the `Vec<String>`'s content to avoid moving into the `for` loop
|
3 | for name in &names {
| +The phrase to notice is "moved due to this implicit call to .into_iter()". A for loop calls IntoIterator::into_iter on whatever you give it, and for a Vec<T> that method takes the vector by value and yields each element by value, consuming the vector. One character fixes it, and the compiler spells it out: iterate over &names, which yields references and leaves the vector intact. Use &mut names if the loop needs to change the elements. Iterate by value only when you want to consume the collection, for example to move each element into another structure.
A closure or thread that takes ownership
The third form shows up with threads, where move closures are required:
use std::thread;
fn main() {
let data = vec![1, 2, 3];
let handle = thread::spawn(move || {
println!("{:?}", data);
});
handle.join().unwrap();
println!("{}", data.len());
}error[E0382]: borrow of moved value: `data`
--> e0382c.rs:9:20
|
4 | let data = vec![1, 2, 3];
| ---- move occurs because `data` has type `Vec<i32>`, which does not implement the `Copy` trait
5 | let handle = thread::spawn(move || {
| ------- value moved into closure here
6 | println!("{:?}", data);
| ---- variable moved due to use in closure
...
9 | println!("{}", data.len());
| ^^^^ value borrowed here after move
|
help: consider cloning the value before moving it into the closurethread::spawn requires a 'static closure, because the new thread may outlive the function that created it, so the closure must own what it uses. move transfers data into the closure, and the main thread loses it. The compiler suggests cloning. For a small vector that is fine. For data you want both threads to read without copying it, share ownership with Arc, a reference-counted pointer that is cheap to clone because it copies only the pointer:
use std::sync::Arc;
use std::thread;
fn main() {
let data = Arc::new(vec![1, 2, 3]);
let for_thread = Arc::clone(&data);
let handle = thread::spawn(move || {
println!("thread sees {:?}", for_thread);
});
handle.join().unwrap();
println!("main still has {} items", data.len());
}thread sees [1, 2, 3]
main still has 3 itemsArc::clone(&data) does not copy the vector. It increments a counter, and both handles point at the same allocation, freed when the last one is dropped. If the threads must modify the data, wrap it in a Mutex inside the Arc.
Choosing between borrow, clone and share
| Situation | Reach for |
|---|---|
| The callee only reads the value | Borrow: take &T or &str and pass &value |
| The callee needs its own copy to store or change | .clone() at the call site, or take T and let callers decide |
| Several owners must read the same large data | Rc<T> on one thread, Arc<T> across threads |
The value is small and plain (numbers, bool, char, tuples of those) | Nothing: these are Copy and never move |
A rule of thumb that prevents most of these errors: functions should take references unless they need ownership, and when they need ownership, the signature should say so by taking T. A signature that takes String just to read it forces every caller to clone or give up the value.
Variants that look different and are the same error
The same error appears in a few other shapes. A value moved inside a loop body fails on the second iteration: for _ in 0..3 { consume(name); } reports "value moved here, in previous iteration of loop". A value moved in one branch of an if and used after the if reports a "value moved here" in the branch. A method that takes self, such as into_iter, unwrap or into_boxed_slice, moves the receiver, and the later use of the variable fails. A struct field moved out with let a = s.field; partially moves s, so using s afterward fails with "borrow of partially moved value". Each has the same shape: find the line marked "value moved here", ask whether that call needed ownership, and apply the table above. Often it did not, and changing a parameter from T to &T, or into_iter() to iter(), removes the error at its source.
How to stop meeting it
Read the first note in the message, not just the suggestion at the bottom. When it says "consider changing this parameter type to borrow instead", that is the design hint, and following it makes the code better, not just quieter. Prefer &str and &[T] in signatures over String and Vec<T> for read-only parameters. Use iter() and &collection in loops unless you want to consume. And learn Copy: integers, floats, bool, char and shared references are copied on use, which is why let b = a; println!("{a}") works for a number and fails for a string. For the concept behind all of this, read ownership in Rust explained with a mental model that holds, and for the sibling errors that appear when you borrow instead, see E0502 and E0499.


