rust: cannot borrow `*self` as mutable because it is also borrowed as immutable
Olivia Martin@liv_codes
making the pong game for @tomasz_k (long story) and im stuck on the borrow checker. i have a game struct with a list of entities and i want to update each one, but update needs to call a method on self
struct Game {
items: Vec<Entity>,
score: u32,
}
impl Game {
fn tick(&mut self) {
for item in self.items.iter() {
self.update(item);
}
}
fn update(&mut self, item: &Entity) {
// moves the item, maybe changes score
}
}
i kind of get WHY (im iterating over items while self could change items) but i dont know the normal way to fix it. clone the vec? that feels wrong
Accepted answer
Classic one. The loop holds a borrow of self.items, and update wants all of self mutably, which includes items. A few normal fixes:
1. Make update not take &mut self. If it only needs the score, pass that:
fn update(item: &mut Entity, score: &mut u32)
then
for item in self.items.iter_mut() { Self::update(item, &mut self.score); }
The compiler is fine with borrowing two different fields at the same time.
2. Loop by index: for i in 0..self.items.len() and use self.items[i]. Works, but it gets ugly fast.
3. Take the vec out temporarily: let mut items = std::mem::take(&mut self.items); loop over it, then put it back. Useful when update really needs the whole self.
Option 1 is usually the idiomatic answer: split the data so each function borrows only what it needs.
Written by
Olivia Martin
final year cs student @ toronto. looking for a summer internship (hire me pls). currently obsessed with rust and making my resume not look like a resume
Get the best of #rubber-duck-club weekly
One email each Monday with the most useful posts. No spam; unsubscribe in one click.
Comments
Sign in to join the conversation.
Sign in to commentAccepted answer
Arjun Mehta@arjunmehta
Classic one. The loop holds a borrow of self.items, and update wants all of self mutably, which includes items. A few normal fixes:
1. Make update not take &mut self. If it only needs the score, pass that:
fn update(item: &mut Entity, score: &mut u32)
then
for item in self.items.iter_mut() { Self::update(item, &mut self.score); }
The compiler is fine with borrowing two different fields at the same time.
2. Loop by index: for i in 0..self.items.len() and use self.items[i]. Works, but it gets ugly fast.
3. Take the vec out temporarily: let mut items = std::mem::take(&mut self.items); loop over it, then put it back. Useful when update really needs the whole self.
Option 1 is usually the idiomatic answer: split the data so each function borrows only what it needs.
Olivia Martin@liv_codes
option 1 worked!! passing the score separately and the compiler just. accepted it. the borrow checker is way less scary once u realise its about fields
Tomasz Kowalski@tomasz_k
Pong for me, with borrow checker. This is more effort than my last three Java projects. I am honoured.
Olivia Martin@liv_codes
u better be. there's a paddle named after u. it moves slowly and judges everyone