Diagnosing Rust Compiler Error E0382: Use of Moved Value
A concise diagnostic guide for Rust error E0382: recognize the condition, check causes, apply fixes, and know when to redesign ownership.
26 Dec 2025, 13:18 UTC

Recognizable condition
The Rust compiler emits error E0382 with a message similar to "value used here after move" when you try to read a variable after its ownership has been transferred elsewhere. The error points to the line where the value is used, not where it was moved.
Cause / diagnostic table
| Cause | Typical code pattern | Error location (example) |
|---|---|---|
| Passing by value to a function | let s = String::from("hi"); foo(s); println!("{}", s); | The println! line – compiler notes "value used here after move" |
| Assignment to another variable | let s = String::from("hi"); let t = s; println!("{}", s); | The println! line |
| Storing in a collection that takes ownership | let s = String::from("hi"); let v = vec![s]; println!("{}", s); | The println! line |
| Returning from a function that moves out | fn foo() -> String { let s = String::from("hi"); s } let x = foo(); println!("{}", x); | The second println! line (if you try to use s after the return) |
Ordered checks
- Locate the error line – open the file at the line and column indicated by
cargo checkor your IDE. - Trace the last move – look backward from that line for any assignment, function call, or collection insertion that takes ownership of the variable.
- Ask if a reference suffices – could the code work with
&Tor&mut Tinstead of moving the value? - Check for
Copy– if the type implementsCopy(e.g., primitive integers,bool,char, tuples of such types), the move is actually a copy and the error should not appear; verify with:? std::marker::Copyin the docs. - Consider cloning – if ownership is needed elsewhere and the type is not
Copy, does cloning make sense for your use case?
Fixes tied to findings
When the type is Copy
No change is required; the error is likely a false positive caused by a misleading note. Verify by adding an explicit type annotation or checking the trait implementation.
When a borrow is sufficient
Replace the move with a reference:
// before
let s = String::from("hi");
foo(s); // takes ownership
println!("{}", s); // error
// after
let s = String::from("hi");
foo(&s); // foo now accepts &String
println!("{}", s);
Adjust the function signature accordingly (fn foo(x: &String) { … }).
When ownership must be retained
Clone the value if the cost is acceptable:
let s = String::from("hi");
let t = s.clone(); // cheap for small strings, expensive for large buffers
println!("{}", s); // OK
For shared ownership without deep cloning, use reference‑counted pointers:
use std::rc::Rc;
let s = Rc::new(String::from("hi"));
let t = Rc::clone(&s); // increments the ref‑count
println!("{}", s); // OK
In multithreaded contexts replace Rc with std::sync::Arc.
Escalation criteria
If applying the above fixes leads to:
- Excessive cloning in hot paths (profile with
cargo benchor a profiler). - Lifetime complications that cause borrowing errors elsewhere.
- Complex reference‑counted graphs risking cycles.
Consider redesigning ownership:
- Move the data into a struct that owns it and pass the struct by reference.
- Use interior mutability (
std::cell::CellorRefCell) when you need mutable access through shared references. - Choose a different data structure (e.g., indices into a vector or an arena allocator) to avoid moving large values.
If the error persists after these steps, re‑examine the control flow for hidden moves—such as matches that bind by value, or closures that capture variables by move—and apply the same borrowing or cloning strategy to those sites.
Verification steps (for your own testing)
- Create a minimal project:
cargo new demo && cd demo. - Insert a snippet that triggers E0382, for example:
let s = String::from("hi"); let t = s; println!("{}", s); - Run
cargo check; you should see the error pointing at theprintln!line. - Apply a fix (e.g., change the assignment to
let t = s.clone();) and runcargo checkagain; the error should disappear. - If you used a borrow, ensure the function signature matches and run
cargo runto confirm the program behaves as intended. - For the
Rcalternative, replace theStringwithRc<String>, clone theRc, and verify withcargo checkandcargo run.
Note: These steps are provided as a guide; you should adapt them to your specific codebase and verify the results yourself.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.