What happens to the original variable after a move?
Short answer: nothing is done to it. No destructor runs, no memory is erased — the binding stops counting as initialised, and the compiler refuses to let you read it. The value itself is still alive under the new name, and will be dropped once, when that name goes out of scope.
Nothing runs at the move
Section titled “Nothing runs at the move”Give the value a destructor that prints, and you can watch the move happen:
struct Noisy(&'static str);
impl Drop for Noisy { fn drop(&mut self) { println!("dropping {}", self.0); }}
fn main() { let first = Noisy("payload"); println!("before the move");
let second = first;
println!("after the move"); println!("second holds {}", second.0);}before the moveafter the movesecond holds payloaddropping payloadOne dropping line, and it comes last — at the closing brace of main, not at the move. second is the
binding that destructor runs for now. Nothing printed at let second = first; because there was nothing to
do there: it’s the same value, one name further along.
It is uninitialised, not holding garbage
Section titled “It is uninitialised, not holding garbage”A moved-from binding doesn’t hold a leftover value that some other route could still reach. It doesn’t hold
a value at all. The Rust Reference names the state in
a comment on its own example: // Drops now, but is then uninitialized. The compiler tracks exactly that
fact, one binding at a time.
So assigning to the binding again is legal, and it’s the ordinary way to get back to work:
fn main() { let mut message = String::from("hello"); let greeting = message;
message = String::from("world");
println!("{greeting} then {message}");}hello then worldThat assignment destroys nothing, because there was nothing there to destroy. The Reference’s rule is “Assignment also runs the destructor of its left-hand operand, if it’s initialized” — and a moved-from binding isn’t.
Reading it is what fails
Section titled “Reading it is what fails”Using the old name before re-initialising it is rejected, and the diagnostic points at both ends:
fn main() { let message = String::from("hello"); let greeting = message;
println!("{greeting}"); println!("{}", message.len());}error[E0382]: borrow of moved value: `message` --> src/main.rs:6:20 |2 | let message = String::from("hello"); | ------- move occurs because `message` has type `String`, which does not implement the `Copy` trait3 | let greeting = message; | ------- value moved here...6 | println!("{}", message.len()); | ^^^^^^^ value borrowed here after move |help: consider cloning the value if the performance cost is acceptable |3 | let greeting = message.clone(); | ++++++++
For more information about this error, try `rustc --explain E0382`.Two carets, two facts: value moved here at the assignment, value borrowed here after move at the use.
The error lands on the line that used the name, which is usually further down than the move that caused it
— that gap is why the message looks like it’s blaming the wrong line. The help: line offers clone();
the article on moves has the other ways out.
Re-initialising on one path is not enough
Section titled “Re-initialising on one path is not enough”Since the fact being tracked is initialisation, it’s tracked per path. Put the assignment inside an if
and the binding is still uninitialised on the other branch:
fn main() { let mut message = String::from("hello"); let greeting = message;
if greeting.len() > 3 { message = String::from("world"); }
println!("{greeting} {message}");}error[E0382]: borrow of moved value: `message` --> src/main.rs:9:27 |2 | let mut message = String::from("hello"); | ----------- move occurs because `message` has type `String`, which does not implement the `Copy` trait3 | let greeting = message; | ------- value moved here...6 | message = String::from("world"); | ------- this reinitialization might get skipped...9 | println!("{greeting} {message}"); | ^^^^^^^ value borrowed here after move |help: consider cloning the value if the performance cost is acceptable |3 | let greeting = message.clone(); | ++++++++
For more information about this error, try `rustc --explain E0382`.this reinitialization might get skipped is the compiler saying what it’s tracking. Remove the branch and
the same statements compile:
fn main() { let mut message = String::from("hello"); let greeting = message;
message = String::from("world");
println!("{greeting} {message}");}hello worldRelated
Section titled “Related”- Why does Rust move values instead of copying them? — what the move itself does, and this page’s predecessor
- What is ownership in Rust? — the rule all of this falls out of
- How to fix error E0382: borrow of moved value — every fix, with the trade-offs
- Moves and ownership — the rest of this chapter
- Next in this chapter: how to tell whether a type will move or copy