繁体   English   中英

将可变的自引用传递给拥有对象的方法

[英]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 ,以便能够自我更新。 这不会编译,因为结果将是循环引用与变异结合在一起。 您可以通过使用CellRefCell (后者具有性能成本)来完成这项工作,但是最好以不同的方式构造代码。 Field结构检查并解决Ball - BallBall - 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不会更改(“不可变引用”的不可变部分)。 但是,此代码也试图改变它的一部分:球! 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删除成员

您也可以采用其他方法BallField移开,然后再对其进行任何更改。 如果您可以轻松/廉价地制造一个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;
    }
}

如果您不容易创建新值,则可以使用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);
        }
    }
}

解决方案:运行时检查

您可以通过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)
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM