What happens when you assign one struct to another variable?
Short answer: let backup = person; moves the whole struct into backup — every field at once,
Copy fields included — and person is invalidated. The assignment copies instead of moving only when
the struct’s type implements Copy, which for a struct you wrote means deriving it, over fields that are
all Copy themselves.
The assignment moves every field
Section titled “The assignment moves every field”The previous step, how ownership works with structs, ended
on this line: use the struct as one value and every field goes with it. Assignment is that move. Every block
on this page is a complete src/main.rs; start from any Cargo project (cargo new struct-assign will do —
the output doesn’t depend on the name) and replace the file one block at a time:
struct Person { name: String, age: u32,}
fn main() { let person = Person { name: String::from("Ada"), age: 36, };
let backup = person;
println!("backup: {} is {}", backup.name, backup.age);}backup: Ada is 36age is a u32, a Copy type, and it moved too. The unit of the move is the struct, so the whole binding
is invalidated, not field by field. Read person after the assignment and the compiler answers with the
same E0382 you get from passing the struct to a function:
struct Person { name: String, age: u32,}
fn main() { let person = Person { name: String::from("Ada"), age: 36, };
let backup = person;
println!("backup: {} is {}", backup.name, backup.age); println!("person is gone: {}", person.age);}error[E0382]: borrow of moved value: `person` --> src/main.rs:15:36 | 7 | let person = Person { | ------ move occurs because `person` has type `Person`, which does not implement the `Copy` trait...12 | let backup = person; | ------ value moved here...15 | println!("person is gone: {}", person.age); | ^^^^^^^^^^ value borrowed here after move |note: if `Person` implemented `Clone`, you could clone the value --> src/main.rs:1:1 | 1 | struct Person { | ^^^^^^^^^^^^^ consider implementing `Clone` for this type...12 | let backup = person; | ------ you could clone this value
For more information about this error, try `rustc --explain E0382`.The old binding isn’t cleared; it’s marked uninitialised, which is
what happens to the original variable after a move
in full. Re-initialising it (person = Person { … }) is the way back to work.
When the assignment copies instead
Section titled “When the assignment copies instead”A struct whose type implements Copy is duplicated by the assignment, and both bindings stay usable:
#[derive(Copy, Clone)]struct Point { x: i32, y: i32,}
fn main() { let a = Point { x: 1, y: 2 }; let b = a;
println!("a is still here: {} {}", a.x, a.y); println!("b: {} {}", b.x, b.y);}a is still here: 1 2b: 1 2Two conditions, and both have to hold. Every field has to be Copy — one String field and the struct
moves again — and the derive has to be written, because your own structs are never Copy until you ask for
it. How to tell whether a type will move or copy
has the test, and what blocks the derive.
Assigning into one field
Section titled “Assigning into one field”The other shape of struct assignment writes to a field, and that moves one field, not the struct:
struct Person { name: String, age: u32,}
fn main() { let mut target = Person { name: String::from("Grace"), age: 45, }; let source = Person { name: String::from("Ada"), age: 36, };
target.name = source.name;
println!("target is whole: {} is {}", target.name, target.age); println!("source.age still works: {}", source.age);}target is whole: Ada is 45source.age still works: 36target never lost a field, so it stays usable as one value; the old name was dropped by the assignment
and the new one put in its place. source is the one that changed — its name left, which leaves the
struct partially moved, and what is a partial move? covers
what that does to the rest of it.
If you need both structs
Section titled “If you need both structs”Assignment gives the struct away. When the copy is the point — a snapshot to fall back on — clone it:
#[derive(Clone)]struct Person { name: String, age: u32,}
fn main() { let person = Person { name: String::from("Ada"), age: 36, }; let backup = person.clone();
println!("person: {} is {}", person.name, person.age); println!("backup: {} is {}", backup.name, backup.age);}person: Ada is 36backup: Ada is 36#[derive(Clone)] is what makes person.clone() exist. When should you clone?
is about when that’s the right answer, and how to fix error E0382
collects every fix for the error above.
Related
Section titled “Related”- How ownership works with structs — fields move independently of the struct
- What is a partial move? — one field leaves, and the whole struct is off-limits
- How to tell whether a type will move or copy —
the
Copytest, and how to run it yourself - Moves and ownership — the rest of this chapter
- Next in this chapter: how ownership works with match expressions