Rust E0382 — use of moved value
E0382: use of moved value
Verified against The Rust Book: Chapter 4 (Ownership), The Rust Reference: Move semantics, rustc error index: E0382 · Updated June 2026
> quick_fix
Once a value is moved into a variable or function, the original binding is invalidated — Rust has a single-owner rule. Fix by: (1) passing a reference `&val` instead of moving `val`, (2) calling `.clone()` before moving if you need to keep a copy, or (3) using a `Copy` type (integers, booleans) which copy automatically.
let s = String::from("hello");
let s2 = s; // s is moved to s2
println!("{}", s); // ERROR E0382: use of moved value: `s`
// Fix A — clone before moving
let s = String::from("hello");
let s2 = s.clone();
println!("{}", s); // OK — s still valid
// Fix B — borrow instead of move
let s = String::from("hello");
let s2 = &s; // borrow, not move
println!("{}", s); // OKWhat causes this error
Rust's ownership model: every value has exactly one owner. When you assign a value to a new variable or pass it to a function, ownership is transferred (moved) — the original binding is no longer valid. Types that implement the `Copy` trait (all primitive integers, floats, booleans, char, tuples/arrays of Copy types) copy automatically and don't trigger this error. Types that manage heap memory (`String`, `Vec<T>`, `Box<T>`) are not Copy and are moved instead.
How to fix it
- 01
step 1
Pass a reference instead of moving the value
If the function only needs to read the value (not own it), change the parameter from `T` to `&T`. This borrows the value without moving it.
// Takes ownership — caller loses the value fn print_name(name: String) { println!("{}", name); } let name = String::from("Alice"); print_name(name); // name moved here println!("{}", name); // ERROR: use of moved value // Fix — borrow with &str or &String fn print_name(name: &str) { println!("{}", name); } let name = String::from("Alice"); print_name(&name); // borrow println!("{}", name); // OK - 02
step 2
Clone when you need both the original and a copy
`clone()` creates a deep copy of heap-allocated data. Use it when you genuinely need two independent copies of the data.
let original = vec![1, 2, 3]; let copy = original.clone(); // explicit deep copy process(original); // original is moved here println!("{:?}", copy); // copy is still valid - 03
step 3
Use Copy types when possible
If your custom type contains only Copy fields and doesn't manage heap memory, derive Copy so it copies automatically like integers do.
// Without Copy — moved on assignment struct Point { x: f64, y: f64 } let p1 = Point { x: 1.0, y: 2.0 }; let p2 = p1; // p1 moved println!("{}", p1.x); // ERROR // With Copy + Clone — copied on assignment #[derive(Copy, Clone)] struct Point { x: f64, y: f64 } let p1 = Point { x: 1.0, y: 2.0 }; let p2 = p1; // p1 is copied, not moved println!("{}", p1.x); // OK - 04
step 4
Fix move in closures — use move keyword or clone before capture
Closures capture variables from the environment. If a closure moves a value and you use the variable afterward, it's an E0382. Use `move` explicitly and clone the value before the closure if needed.
let name = String::from("Alice"); let greet = move || println!("Hello, {}", name); // name moved into closure println!("{}", name); // ERROR: use of moved value // Fix — clone before the move closure let name = String::from("Alice"); let name_clone = name.clone(); let greet = move || println!("Hello, {}", name_clone); // clone moved println!("{}", name); // OK — original still valid
How to verify the fix
- `cargo build` passes without E0382 errors.
- No unnecessary `.clone()` calls on Copy types (the compiler warns via clippy).
- `cargo clippy` shows no clone-on-copy warnings.
Why E0382 happens at the runtime level
Rust's ownership model assigns each value a unique owner. Assignment and function calls transfer ownership (move) by default for non-Copy types. The compiler tracks ownership via a flow analysis pass in MIR (Mid-level Intermediate Representation). After a move, the original binding is marked as 'moved-out-of' and any subsequent use generates E0382. The MIR pass handles partial moves (moving a field out of a struct), conditional moves (moves inside if branches), and moves in loops. The restriction on reuse after move is what enables Rust to insert exactly one `drop()` call per value with no runtime reference counting.
Common debug mistakes for E0382
- Passing `String` to a function instead of `&str` — most string consumers only need a borrow, not ownership.
- Moving a value into a `for` loop body that iterates multiple times — the value is moved on the first iteration.
- Moving `self` in an `impl` method when `&self` or `&mut self` is sufficient.
- Capturing a non-Copy value in multiple closures — each closure that uses `move` moves the value once, leaving it invalid for subsequent closures.
- Using `into_iter()` instead of `iter()` on a collection — `into_iter()` moves elements out, `iter()` borrows them.
When E0382 signals a deeper problem
Frequent E0382 errors across a codebase indicate that the ownership model hasn't been internalized. The idiomatic Rust pattern is: functions take references (`&T` or `&mut T`) when they don't need ownership, and take owned values (`T`) only when they will store or transfer the value. An API that takes `String` everywhere instead of `&str` forces callers to clone unnecessarily. Reviewing all function signatures for unnecessary ownership requirements — especially in library code — is a standard Rust API review step. The `clippy::needless_pass_by_value` lint flags functions that take owned types but only use them by reference.
Editor's take
E0382 is the most common Rust error for developers coming from garbage-collected languages. In Python, JavaScript, or Java, you can use a value as many times as you want after assigning it — the runtime handles memory. In Rust, the language itself enforces that values have one owner, and the error makes this explicit at the point of violation.
The fastest path to understanding is to internalize the two patterns: pass by reference (`&T`) for reading, pass by owned value (`T`) for storing or transferring. Library APIs should almost always take `&str` not `String`, `&[T]` not `Vec<T>`, `&Path` not `PathBuf`. This is because callers should not be forced to clone — they can always coerce an owned type to a reference (`String` -> `&str` with `&name` or `name.as_str()`), but they can't go the other direction without allocating.
The move-in-loop case is particularly confusing for beginners. A `for item in collection` loop moves `collection` into the iterator. Inside the loop, `item` is moved on each iteration. If you try to pass `item` to a function that takes ownership, and then access `item` again in the same loop body, it's moved. The fix: either change the consuming function to take `&T`, or call `.iter()` instead of `into_iter()` to borrow each element. The difference between `for x in vec` (moves elements), `for x in &vec` (borrows elements), and `for x in &mut vec` (mutably borrows elements) is one of the first Rust patterns worth memorizing.
By Bikram Nath · Curator · Updated June 2026
Frequently asked questions
What is the difference between Copy and Clone?
Copy is a marker trait that enables implicit bitwise copy on assignment — `let b = a` copies the value without moving. It requires that all data is stack-allocated (no heap pointers). Clone is an explicit operation (`a.clone()`) that can be arbitrarily complex — for `Vec`, it allocates new heap memory and copies all elements. `Copy: Clone` (every Copy type must also implement Clone), but not vice versa.
Why doesn't Rust just copy automatically like other languages?
Implicit copying of heap-allocated data has two hidden costs: memory allocation and time. A `String` containing 10MB copied implicitly every time you pass it to a function would be a performance disaster. Rust makes the cost visible and explicit: `clone()` at the call site shows the programmer that a heap allocation is happening. Move semantics allow zero-copy passing of owned data, which is often what you actually want.
Can I move out of a borrowed value?
No. You cannot move out of `*reference` unless the type is Copy. This prevents the referenced data from being left in an invalid state. If you need to extract an owned value from a reference, use `.clone()` or replace the referenced value using `std::mem::replace` or `std::mem::take`.