TL;DR
You used a value after it was moved into another variable or function. Rust's ownership system invalidates the original binding after a move. Two quick fixes: call .clone() before the move, or pass a reference (&value) instead of the value itself.
// BROKEN
let s = String::from("hello");
let s2 = s; // s is moved here
println!("{}", s); // error[E0382]: use of moved value: `s`
// FIX 1: clone
let s = String::from("hello");
let s2 = s.clone();
println!("{}", s); // OK
// FIX 2: borrow
let s = String::from("hello");
let s2 = &s;
println!("{}", s); // OK
What's actually happening
Every heap-allocated value in Rust has exactly one owner at a time. Assign s2 = s, or pass s into a function โ ownership transfers. Rust calls this a move. After the move, s is gone. The compiler won't let you use a ghost.
Here's what the full error looks like:
error[E0382]: use of moved value: `s`
--> src/main.rs:4:20
|
2 | let s2 = s;
| - value moved here
3 | println!("{}", s);
| ^ value used here after move
|
= note: move occurs because `s` has type `String`, which does not implement the `Copy` trait
That last line is the key. Types implementing Copy โ integers, booleans, chars, tuples of Copy types โ are silently copied instead of moved. String, Vec, HashMap, and most heap types don't implement Copy, so they move.
Common scenarios and fixes
Scenario 1: Moving into a function
fn save_to_db(name: String) { /* consumes name */ }
let name = String::from("Alice");
save_to_db(name); // name moved into save_to_db
println!("{}", name); // error[E0382]: use of moved value: `name`
Fix A: Change the function signature to borrow instead of own:
fn save_to_db(name: &str) { /* borrows name */ }
save_to_db(&name);
println!("{}", name); // OK
Fix B: Clone before passing if you can't change the function:
save_to_db(name.clone());
println!("{}", name); // OK
Scenario 2: Moving in a loop
This one bites constantly at 2 AM:
let data = vec![1, 2, 3];
for _ in 0..3 {
process(data); // error[E0382]: use of moved value: `data`
// moved on first iteration, gone after
}
fn process(v: Vec<i32>) { /* consumes v */ }
Fix A: Pass a reference if the function doesn't need ownership:
fn process(v: &[i32]) { /* borrows v */ }
for _ in 0..3 {
process(&data); // OK
}
Fix B: Clone inside the loop only when the function must own the value:
for _ in 0..3 {
process(data.clone());
}
Scenario 3: Moving into a closure
let config = Config::new();
let handler = move || {
println!("{:?}", config); // config moved into closure
};
println!("{:?}", config); // error[E0382]: use of moved value
Fix: Clone before the move closure captures it, so each side has its own copy:
let config = Config::new();
let config_clone = config.clone();
let handler = move || {
println!("{:?}", config_clone);
};
println!("{:?}", config); // OK
Scenario 4: Partial moves from struct fields
struct User {
name: String,
email: String,
}
let user = User {
name: String::from("Alice"),
email: String::from("alice@example.com"),
};
let name = user.name; // partial move!
println!("{}", user.email); // OK โ email not moved
println!("{}", user.name); // error[E0382]: partial move
Fix: Clone the field so the struct stays intact:
let name = user.name.clone();
println!("{}", user.name); // OK
Scenario 5: match consuming a value
let opt = Some(String::from("data"));
match opt {
Some(s) => println!("Got: {}", s), // s moved out of opt
None => {}
}
println!("{:?}", opt); // error[E0382]
Fix: Match on a reference โ add & before opt:
match &opt {
Some(s) => println!("Got: {}", s),
None => {}
}
println!("{:?}", opt); // OK
When clone() is too expensive
Cloning a 100 MB Vec in a hot loop will tank performance. Before reaching for .clone(), consider these alternatives:
- Arc โ shared ownership across threads. Cloning it just bumps an atomic counter, not the data.
- Rc โ same idea, single-threaded only.
- Cow โ borrows until a mutation is needed, then clones lazily.
use std::sync::Arc;
let data = Arc::new(vec![1, 2, 3]);
let data2 = Arc::clone(&data); // cheap pointer clone, not a data copy
std::thread::spawn(move || {
println!("{:?}", data2);
});
println!("{:?}", data); // OK
Verification
After applying your fix, check whether E0382 is gone:
cargo build 2>&1 | grep "E0382"
No output means you're clear. For a full clean rebuild:
cargo clean && cargo build
Run Clippy too โ it often points toward a more idiomatic solution than manual cloning:
cargo clippy
Decision guide
- Function only reads the value โ pass
&T - Function needs to modify it โ pass
&mut T - Function needs ownership and you need the original โ
.clone() - Value shared across threads โ
Arc<T> - Value shared in single-threaded code โ
Rc<T> - Type is small and stack-only โ derive
Copy
#[derive(Copy, Clone)]
struct Point { x: f64, y: f64 }
let p = Point { x: 1.0, y: 2.0 };
let p2 = p; // copied, not moved
println!("{}", p.x); // OK

