Passing mutable self reference to method of owned object
Solution 1:
Currently your Ball
struct needs to know about the Field
it's contained in to be able to update itself. This doesn't compile because the result would be cyclic references combined with mutation. You could make this work by using Cell
or RefCell
(the latter having a performance cost) but it would be even better to structure the code differently. Let the Field
struct check for and resolve Ball
-Ball
and Ball
-Wall
collisions. The Ball
struct's update
function can handle updating the Ball
's position.
// Ball's update function
fn update(&mut self) {
// update position
}
// Field's update function
fn update(&mut self) {
for ball in self.balls.iter_mut() {
ball.update();
}
// check for collisions
// resolve any collisions
}
Solution 2:
Here's a smaller example:
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 problem
When you pass in a reference to Field
, you are making the guarantee that the Field
cannot change (the immutable part of "immutable reference"). However, this code is also attempting to mutate a part of it: the ball! Which reference should be authoritative, self
or field
, in the implementation of Ball::update
?
Solution: use only the fields you need
You can separate the parts of the structure needed for update
and those not needed and use them before calling the update
function:
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 bundle these piecemeal references up into a tidy 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 struct to separate the information from the beginning:
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)
}
}
Solution: remove the member from self
You could also go the other way and remove the Ball
from the Field
before making any changes to it. If you can easily / cheaply make a Ball
, try replacing it:
use std::mem;
#[derive(Default)]
struct Ball {
size: u8,
}
impl Ball {
fn update(&mut self, field: &Field) {}
}
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 can't easily make a new value, you can use an 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);
}
}
}
Solution: runtime checks
You can move borrow checking to runtime instead of compile-time via RefCell
:
use std::cell::RefCell;
struct Ball {
size: u8,
}
impl Ball {
fn update(&mut self, field: &Field) {}
}
struct Field {
ball: RefCell<Ball>,
}
impl Field {
fn update(&mut self) {
self.ball.borrow_mut().update(self)
}
}