How to borrow one field of a struct
Short answer: Borrow the field, not the struct: &player.name borrows only that field, so a
&mut player.score can be alive at the same time. The borrow checker tracks the fields of a struct
separately — until a method call, whose self is the whole struct. player.double() borrows all of
player, no matter which fields double actually touches.
Two fields, two borrows
Section titled “Two fields, two borrows”Start from a fresh project — cargo new borrow-field, then replace src/main.rs with the whole of
each file below.
struct Player { name: String, score: u32,}
fn main() { let mut player = Player { name: String::from("Ada"), score: 0, };
let name = &player.name; let score = &mut player.score; *score += 100;
println!("{name} has {score} points");}Ada has 100 pointsA shared borrow of player.name and an exclusive borrow of player.score are alive at the same
time, and the compiler approves. They point at different fields, so they never overlap: this is
called a disjoint borrow. The rule “one mutable borrow, or many shared ones” applies per place —
and a field is its own place, separate from every other field and from the struct as a value.
Two fields, two arguments
Section titled “Two fields, two arguments”The same split works through a function. transfer takes two &mut u32 values, and at the call
site they borrow two different fields:
struct Player { score: u32, bonus: u32,}
fn transfer(from: &mut u32, to: &mut u32) { let amount = *from; *from = 0; *to += amount;}
fn main() { let mut player = Player { score: 50, bonus: 0, };
transfer(&mut player.score, &mut player.bonus); println!("score {}, bonus {}", player.score, player.bonus);}score 0, bonus 50Two exclusive borrows of player are alive during the call, and it still compiles — because one
covers player.score and the other covers player.bonus. Inside transfer there is no way to tell
that the two integers came from the same struct; the splitting happened where the borrows were
written, in the caller.
The same field twice
Section titled “The same field twice”Point both arguments at the same field and the split is gone:
struct Player { score: u32, bonus: u32,}
fn transfer(from: &mut u32, to: &mut u32) { let amount = *from; *from = 0; *to += amount;}
fn main() { let mut player = Player { score: 50, bonus: 0, };
transfer(&mut player.score, &mut player.score);}error[E0499]: cannot borrow `player.score` as mutable more than once at a time --> src/main.rs:18:33 |18 | transfer(&mut player.score, &mut player.score); | -------- ----------------- ^^^^^^^^^^^^^^^^^ second mutable borrow occurs here | | | | | first mutable borrow occurs here | first borrow later used by call
For more information about this error, try `rustc --explain E0499`.The two arguments now borrow the same place, so one of them has to lose. The diagnostic says it
plainly: the first borrow of player.score is still live — it is on its way into the call — when
the second one starts.
The method call that borrows all of it
Section titled “The method call that borrows all of it”Field borrows get one big exception, and it looks arbitrary at first. Here double only touches
self.score, one field — yet the call conflicts with a borrow of name:
struct Player { name: String, score: u32,}
impl Player { fn double(&mut self) { self.score *= 2; }}
fn main() { let mut player = Player { name: String::from("Ada"), score: 10, };
let name = &player.name; player.double(); println!("{name}");}error[E0502]: cannot borrow `player` as mutable because it is also borrowed as immutable --> src/main.rs:19:5 |18 | let name = &player.name; | ------------ immutable borrow occurs here19 | player.double(); | ^^^^^^^^^^^^^^^ mutable borrow occurs here20 | println!("{name}"); | ---- immutable borrow later used here
For more information about this error, try `rustc --explain E0502`.The reason is in the signature, not the body. player.double() means Player::double(&mut player):
the method takes &mut self, and self is the whole Player, so the call borrows every field at
once. What a method borrows is decided by its signature — the compiler does not split a borrow based
on which fields the body happens to use. Inside double, self.score borrows through self, which
already owns the exclusive borrow of everything.
End the borrow before the call
Section titled “End the borrow before the call”The fix is the last-use rule from the previous article: the borrow of player.name ends at its last
read, so put that read before the call.
struct Player { name: String, score: u32,}
impl Player { fn double(&mut self) { self.score *= 2; }}
fn main() { let mut player = Player { name: String::from("Ada"), score: 10, };
let name = &player.name; println!("{name}");
player.double(); println!("{} has {} points", player.name, player.score);}AdaAda has 20 pointsThe method call sits after the last use of name, so the borrows never overlap. When the field
borrow genuinely has to stay live across a method call, the way out is to stop borrowing the whole
struct — pass the field, or hand out the value — which is the subject of the next article.
Related
Section titled “Related”- What are the rules of borrowing? — the two rules the field split lives inside
- Why does Rust allow many immutable borrows but only one mutable? — why two borrows have to overlap to be a problem at all
- How long does a borrow last? — the last-use rule that fixes the method call above
- Borrowing — the rest of this chapter
- Next in this chapter: when to take a reference instead of cloning