通过可变的自引用方法拥有对象

下面是一个简单的模拟,其中一个场地是一个矩形区域,两个球在其中弹跳。 Field结构体有一个update方法,它调用每个球的update 。 在update方法中,球需要根据其速度移动。 但他们也需要相互作出反应,以及领域的界限:

 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); } } 

如何获得有关边界和其他球的信息到Ball结构的更新函数? Field::update中的这些行不能编译:

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

给出以下错误:

 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 

我明白,但我不知道如何解决这个问题。

目前你的Ball结构需要知道它所包含的Field能够自行更新。 这不会编译,因为结果将是循环引用结合变异。 您可以使用CellRefCell (后者具有性能成本)来完成这项工作,但以不同方式构build代码会更好。 让Field结构检查并解决BallBallBallWall碰撞。 Ball结构的update函数可以处理更新Ball的位置。

 // 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 } 

这是一个小例子:

 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) } } 

根本问题是当你传递一个对Field的引用时,你保证Field不能改变(“不可变引用”的不可变部分)。 但是,你也试图改变它的一部分:球! 在执行Ball::update ,哪个引用应该是权威的, self还是field

一种解决scheme是将update所需的结构部分分开,并调用update函数之前使用它们:

 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) } } 

你甚至可以把这些零散的参考文件整理成一个整洁的包:

 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) } } 

或者重构你的包含结构来从头开始分离信息:

 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) } } 

在做出任何改变之前,你也可以用一种方式去除FieldBall 。 如果您可以轻松/便宜地制作Ball ,请尝试replace它:

 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; } } 

如果你不能轻易做出一个,你可以使用一个Optiontake

 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); } } }