Why can't you use a value after moving it?
Short answer: a move leaves exactly one binding owning the value, and ownership is what decides where the value gets freed. So the old name isn’t stale, it’s unowned — using it again would claim ownership a second time. The compiler rejects that claim while compiling, which is why nothing has to check for it while the program runs.
One owner, one free
Section titled “One owner, one free”Ownership is the compiler’s record of who is responsible for cleaning up. Give the value a destructor that prints and the record becomes something you can count:
struct Buffer(String);
impl Drop for Buffer { fn drop(&mut self) { println!("freeing {:?}", self.0); }}
fn main() { let original = Buffer(String::from("hello")); let moved = original;
println!("moved holds {:?}", moved.0);}moved holds "hello"freeing "hello"One buffer, one freeing line, and it lands after the last statement in main — at the point moved
goes out of scope. The move neither added a free nor removed one; it handed the single free to the new
binding. That half of the story has its own page,
what happens to the original variable after a move.
Two names, two buffers
Section titled “Two names, two buffers”Two freeing lines aren’t the bug. Two names for one buffer would be. Ask for a genuine second buffer
and you get two of each, which is correct:
#[derive(Clone)]struct Buffer(String);
impl Drop for Buffer { fn drop(&mut self) { println!("freeing {:?}", self.0); }}
fn main() { let original = Buffer(String::from("hello")); let copy = original.clone();
println!("original: {:?}", original.0); println!("copy: {:?}", copy.0);}original: "hello"copy: "hello"freeing "hello"freeing "hello"clone allocates, so both names own a buffer of their own and both frees are earned. What the compiler
guards against is the other combination — one buffer with two owners — and a plain second use is how you
would ask for it by accident.
The use it refuses
Section titled “The use it refuses”fn main() { let message = String::from("hello"); let first = message;
let second = message; println!("{first} {second}");}error[E0382]: use of moved value: `message` --> src/main.rs:5:18 |2 | let message = String::from("hello"); | ------- move occurs because `message` has type `String`, which does not implement the `Copy` trait3 | let first = message; | ------- value moved here4 |5 | let second = message; | ^^^^^^^ value used here after move |help: consider cloning the value if the performance cost is acceptable |3 | let first = message.clone(); | ++++++++
For more information about this error, try `rustc --explain E0382`.The second let isn’t reading message, it’s asking to take it — which is why the heading here is
use of moved value rather than the more familiar borrow of moved value. The caret sits on the second
claim, not the first: the assignment on line 3 is legal, and doing it twice is what fails.
Let it through and both names would own the one buffer, so both would free it at the end of main: two
freeing lines for a single allocation. That’s the double free, and refusing the second let is how it
gets prevented.
Nothing checks this at runtime
Section titled “Nothing checks this at runtime”Only one of those two programs got as far as running. The second was rejected while compiling, so it
never produced a binary and there’s no later run in which the mistake can surface. What the compiler
settled up front is which binding owns the value, and therefore where the one drop call in the
compiled program goes. Both halves are visible above: the version that compiled printed freeing once,
and the version that didn’t compile never ran at all.
Genuinely shared ownership is still possible, but it needs a type that counts owners while the program runs — a cost you opt into by naming such a type. Smart pointers and sharing is where those live.
Related
Section titled “Related”- Why does Rust move values instead of copying them? — where this rule first shows up
- What happens to the original variable after a move? — the binding the move leaves behind
- How to fix error E0382: borrow of moved value — borrow, clone, or restructure
- Moves and ownership — the rest of this chapter
- Next in this chapter: why a
Stringisn’tCopy