Passing a mutable self-reference to a method of its object

Below is a simple simulation with a field, which is a rectangular area with two balls bouncing in it. The Field structure has an update method that calls update on each ball. Balls by the update method should move at their speed. But they must also respond to each other, as well as the boundaries of the field .:

 fn main() { let mut field = Field::new(Vector2d { x: 100, y: 100 }); field.update(); } #[derive(Copy, Clone)] struct Vector2d { x: i32, y: i32, } struct Ball { radius: i32, position: Vector2d, velocity: Vector2d, } impl Ball { fn new(radius: i32, position: Vector2d, velocity: Vector2d) -> Ball { Ball { radius: radius, position: position, velocity: velocity, } } fn update(&mut self, field: &Field) { // check collisions with walls // and other objects } } struct Field { size: Vector2d, balls: [Ball; 2], } impl Field { fn new(size: Vector2d) -> Field { let position_1 = Vector2d { x: size.x / 3, y: size.y / 3, }; let velocity_1 = Vector2d { x: 1, y: 1 }; let position_2 = Vector2d { x: size.x * 2 / 3, y: size.y * 2 / 3, }; let velocity_2 = Vector2d { x: -1, y: -1 }; let ball_1 = Ball::new(1, position_1, velocity_1); let ball_2 = Ball::new(1, position_2, velocity_2); Field { size: size, balls: [ball_1, ball_2], } } fn update(&mut self) { // this does not compile self.balls[0].update(self); self.balls[1].update(self); } } 

How do I get information about boundaries and another ball in the update function of the Ball structure? These lines in Field::update do not compile:

 self.balls[0].update(self); self.balls[1].update(self); 

Providing the following error:

 error[E0502]: cannot borrow `*self` as immutable because `self.balls[..]` is also borrowed as mutable --> src/main.rs:62:30 | 62 | self.balls[0].update(self); | ------------- ^^^^- mutable borrow ends here | | | | | immutable borrow occurs here | mutable borrow occurs here 

which I understand, but I don’t know how to get around this.

+7
rust borrow-checker
source share
2 answers

At present, your Ball structure needs to know about the Field in which it is contained in order to be able to update itself. This does not compile because the result will be circular references combined with a mutation. You can do this work using Cell or RefCell (the latter has a cost of execution), but it would be better to structure the code in different ways. Let the Field structure check and resolve Ball - Ball and Ball - Wall conflicts. The Ball struct update function can handle Ball position update.

 // Ball update function fn update(&mut self) { // update position } // Field update function fn update(&mut self) { for ball in self.balls.iter_mut() { ball.update(); } // check for collisions // resolve any collisions } 
+6
source share

Here is an example below:

 struct Ball { size: u8, } impl Ball { fn update(&mut self, field: &Field) {} } struct Field { ball: Ball, } impl Field { fn update(&mut self) { self.ball.update(self) } } 

The root problem is that when you pass a reference to Field you make sure that Field cannot change (the immutable part of the "immutable link"). However, you are also trying to change part of it: the ball! Which link should be reliable, self or Field , in the implementation of Ball::update ?

One solution is to separate parts of the structure necessary for update , and those that are not used and are not used before the update function is called:

 struct Ball { size: u8, } impl Ball { fn update(&mut self, field: &u8) {} } struct Field { players: u8, ball: Ball, } impl Field { fn update(&mut self) { self.ball.update(&self.players) } } 

You can even combine these fragmented links into a neat package:

 struct Ball { size: u8, } impl Ball { fn update(&mut self, field: BallUpdateInfo) {} } struct BallUpdateInfo<'a> { players: &'a u8, } struct Field { players: u8, ball: Ball, } impl Field { fn update(&mut self) { let info = BallUpdateInfo { players: &self.players }; self.ball.update(info) } } 

Or restructure your containing structure to share information from the start:

 struct Ball { size: u8, } impl Ball { fn update(&mut self, field: &UpdateInfo) {} } struct UpdateInfo { players: u8, } struct Field { update_info: UpdateInfo, ball: Ball, } impl Field { fn update(&mut self) { self.ball.update(&self.update_info) } } 

You can also go the other way and remove Ball from Field before making any changes to it. If you can make Ball easily / cheaply, try replacing it:

 use std::mem; struct Ball { size: u8, } impl Ball { fn update(&mut self, field: &Field) {} } impl Default for Ball { fn default() -> Ball { Ball { size: 0 } } } struct Field { ball: Ball, } impl Field { fn update(&mut self) { let mut ball = mem::replace(&mut self.ball, Ball::default()); ball.update(self); self.ball = ball; } } 

If you cannot easily do this, you can use Option and take it:

 struct Ball { size: u8, } impl Ball { fn update(&mut self, field: &Field) {} } struct Field { ball: Option<Ball>, } impl Field { fn update(&mut self) { if let Some(mut ball) = self.ball.take() { ball.update(self); self.ball = Some(ball); } } } 
+1
source share

All Articles