Fix Rust error[E0382]: use of moved value โ€” Ownership and Move Semantics

intermediate๐Ÿฆ€ Rust2026-03-23| Rust 1.60+, all platforms (Linux, macOS, Windows), rustc compiler

Error Message

error[E0382]: use of moved value
#rust#ownership#move#clone

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

Related Error Notes