Skip to content
Select theme

What is a partial move?

Short answer: Moving one field out of a struct moves exactly that field. The others stay usable, but the struct as a whole is finished: with a field missing it can’t be moved or borrowed, so the compiler refuses every use of the whole value and leaves the remaining fields alone.

let name = person.name; takes the name field and nothing else. The rest of the struct stays in place, and the fields that weren’t moved keep working.

Every block on this page is a complete src/main.rs. Start from any Cargo project (cargo new partial-move will do — the output doesn’t depend on the name) and replace the file one block at a time:

name: Ada
nickname: Lady Lovelace

Only the name field moved. nickname is a String too, and it is still there to read: the compiler tracks the fields separately, so one of them leaving says nothing about the other. A Copy field is different — reading person.age where age is a u32 copies the number out, so it never starts a partial move at all.

After the partial move the struct can’t be used as one value any more. Try to move it and the compiler names the state exactly:

error[E0382]: use of partially moved value: `person`
--> src/main.rs:13:17
|
12 | let name = person.name;
| ----------- value partially moved here
13 | let whole = person;
| ^^^^^^ value used here after partial move
|
= note: partial move occurs because `person.name` has type `String`, which does not implement the `Copy` trait
For more information about this error, try `rustc --explain E0382`.

use of partially moved value is the whole diagnosis: the move that caused it is marked value partially moved here, and the note names the field and says why it moved. The code is E0382, the familiar moved-value error with partially inserted.

Borrowing the whole struct is refused too. Here is a debug print after the field is gone:

error[E0382]: borrow of partially moved value: `person`
--> src/main.rs:16:16
|
13 | let name = person.name;
| ----------- value partially moved here
...
16 | println!("{person:?}");
| ^^^^^^ value borrowed here after partial move
|
= note: partial move occurs because `person.name` has type `String`, which does not implement the `Copy` trait
For more information about this error, try `rustc --explain E0382`.

What still works is per-field access: person.nickname compiled fine in the first example. The refusal is on the whole value, not on the struct’s type or its remaining fields.

The compiler doesn’t track “the struct was moved”. It tracks each field’s initialisation separately — the bookkeeping described for whole values, one field down. A struct with a missing field can’t be dropped as a unit: the drop would run a destructor for a field that no longer lives there. So the compiler refuses every use of the whole struct and drops the remaining fields normally at the end of the scope. Give the fields destructors that print and the split becomes visible:

took the name field
dropping name
dropping nickname

name drops first: locals drop in reverse declaration order, and it was declared after person, so it holds the field that moved. Then person drops and its remaining field goes with it. Each destructor runs exactly once.

A struct that implements Drop can’t be partially moved at all

Section titled “A struct that implements Drop can’t be partially moved at all”

If the struct itself has a destructor, none of this is on the table. Drop::drop takes &mut self, so it could read any field — and one of them would already be gone:

error[E0509]: cannot move out of type `Person`, which implements the `Drop` trait
--> src/main.rs:16:16
|
16 | let name = person.name;
| ^^^^^^^^^^^
| |
| cannot move out of here
| move occurs because `person.name` has type `String`, which does not implement the `Copy` trait
|
help: consider borrowing here
|
16 | let name = &person.name;
| +
help: consider cloning the value if the performance cost is acceptable
|
16 | let name = person.name.clone();
| ++++++++
For more information about this error, try `rustc --explain E0509`.

The move itself can’t be made legal: borrow the field (&person.name) or clone it, or keep the value you need out of a Drop type. Moving a field out of a struct you only hold by reference (&Person) is refused for the same reason: a borrower owns nothing to give away. There too the choices are &person.name or person.name.clone(). The Borrowing book covers the details.