Skip to content
Select theme

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.

Give the value a destructor that prints, and you can watch the move happen:

before the move
after the move
second holds payload
dropping payload

One 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.

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:

hello then world

That 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.

Using the old name before re-initialising it is rejected, and the diagnostic points at both ends:

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` trait
3 | 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.

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:

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` trait
3 | 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:

hello world