将可变自引用传递给拥有对象的方法
Passing mutable self reference to method of owned object
下面是一个简单的模拟,场地是一个矩形区域,两个球在其中弹跳。 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
以便能够自我更新。这不会编译,因为结果将是循环引用与变异相结合。您可以使用 Cell
或 RefCell
(后者具有性能成本)来完成这项工作,但以不同方式构建代码会更好。让 Field
结构检查并解决 Ball
-Ball
和 Ball
-Wall
冲突。 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
不能更改([=62 的 immutable 部分=]).然而,这段代码也试图改变它的一部分:球!在Ball::update
的执行中,self
或field
哪个参考应该是权威的?
解决方案:只使用您需要的字段
您可以将 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)
}
}
解决方案:从 self
中删除成员
您也可以采用 other 的方式,在对 Field
进行任何更改之前将 Ball
从 Field
中删除。如果您可以轻松/便宜地制作 Ball
,请尝试更换它:
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;
}
}
如果你不能轻易地创建一个新值,你可以使用一个Option
和take
它:
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);
}
}
}
解决方案:运行时检查
您可以通过 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)
}
}
下面是一个简单的模拟,场地是一个矩形区域,两个球在其中弹跳。 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
以便能够自我更新。这不会编译,因为结果将是循环引用与变异相结合。您可以使用 Cell
或 RefCell
(后者具有性能成本)来完成这项工作,但以不同方式构建代码会更好。让 Field
结构检查并解决 Ball
-Ball
和 Ball
-Wall
冲突。 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
不能更改([=62 的 immutable 部分=]).然而,这段代码也试图改变它的一部分:球!在Ball::update
的执行中,self
或field
哪个参考应该是权威的?
解决方案:只使用您需要的字段
您可以将 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)
}
}
解决方案:从 self
中删除成员
您也可以采用 other 的方式,在对 Field
进行任何更改之前将 Ball
从 Field
中删除。如果您可以轻松/便宜地制作 Ball
,请尝试更换它:
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;
}
}
如果你不能轻易地创建一个新值,你可以使用一个Option
和take
它:
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);
}
}
}
解决方案:运行时检查
您可以通过 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)
}
}