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

以下是一个简单的模拟,其中的场是一个矩形区域,其中有两个弹跳的球。该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

我了解,但是我不知道该如何解决。


慕妹3146593
浏览 422回答 2
2回答

慕尼黑的夜晚无繁华

当前,您的Ball结构需要了解Field它所包含的内容,以便能够自我更新。这不会编译,因为结果将是循环引用与变异结合在一起。您可以通过使用Cell或RefCell(后者会降低性能)来完成这项工作,但最好以不同的方式构造代码。让该Field结构检查并解决Ball- Ball和Ball- Wall冲突。该Ball结构的update函数可以处理更新Ball的位置。// Ball's update functionfn update(&mut self) {    // update position}// Field's update functionfn update(&mut self) {    for ball in self.balls.iter_mut() {        ball.update();    }    // check for collisions    // resolve any collisions}

智慧大石

这是一个较小的示例:struct Ball {&nbsp; &nbsp; size: u8,}impl Ball {&nbsp; &nbsp; fn update(&mut self, field: &Field) {}}struct Field {&nbsp; &nbsp; ball: Ball,}impl Field {&nbsp; &nbsp; fn update(&mut self) {&nbsp; &nbsp; &nbsp; &nbsp; self.ball.update(self)&nbsp; &nbsp; }}问题当您传递对的引用时Field,您将保证Field不能更改(“不可变引用” 的不可变部分)。但是,此代码也试图改变它的一部分:球!self或field在实施中应参考哪个参考Ball::update?解决方案:仅使用您需要的字段您可以将结构所需的部分与不需要的部分分开,update并在调用update函数之前使用它们:struct Ball {&nbsp; &nbsp; size: u8,}impl Ball {&nbsp; &nbsp; fn update(&mut self, field: &u8) {}}struct Field {&nbsp; &nbsp; players: u8,&nbsp; &nbsp; ball: Ball,}impl Field {&nbsp; &nbsp; fn update(&mut self) {&nbsp; &nbsp; &nbsp; &nbsp; self.ball.update(&self.players)&nbsp; &nbsp; }}您甚至可以将这些零星的引用捆绑到一个整齐的包中:struct Ball {&nbsp; &nbsp; size: u8,}impl Ball {&nbsp; &nbsp; fn update(&mut self, field: BallUpdateInfo) {}}struct BallUpdateInfo<'a> {&nbsp; &nbsp; players: &'a u8,}struct Field {&nbsp; &nbsp; players: u8,&nbsp; &nbsp; ball: Ball,}impl Field {&nbsp; &nbsp; fn update(&mut self) {&nbsp; &nbsp; &nbsp; &nbsp; let info = BallUpdateInfo { players: &self.players };&nbsp; &nbsp; &nbsp; &nbsp; self.ball.update(info)&nbsp; &nbsp; }}或重组您的包含结构以将信息与开头分开:struct Ball {&nbsp; &nbsp; size: u8,}impl Ball {&nbsp; &nbsp; fn update(&mut self, field: &UpdateInfo) {}}struct UpdateInfo {&nbsp; &nbsp; players: u8,}struct Field {&nbsp; &nbsp; update_info: UpdateInfo,&nbsp; &nbsp; ball: Ball,}impl Field {&nbsp; &nbsp; fn update(&mut self) {&nbsp; &nbsp; &nbsp; &nbsp; self.ball.update(&self.update_info)&nbsp; &nbsp; }}解决方案:从中删除成员 self您也可以采用其他方法,Ball从中进行删除,Field然后再对其进行任何更改。如果您可以轻松/廉价地制造Ball,请尝试更换它:use std::mem;#[derive(Default)]struct Ball {&nbsp; &nbsp; size: u8,}impl Ball {&nbsp; &nbsp; fn update(&mut self, field: &Field) {}}struct Field {&nbsp; &nbsp; ball: Ball,}impl Field {&nbsp; &nbsp; fn update(&mut self) {&nbsp; &nbsp; &nbsp; &nbsp; let mut ball = mem::replace(&mut self.ball, Ball::default());&nbsp; &nbsp; &nbsp; &nbsp; ball.update(self);&nbsp; &nbsp; &nbsp; &nbsp; self.ball = ball;&nbsp; &nbsp; }}如果您不容易创建新值,则可以使用Optionand take:struct Ball {&nbsp; &nbsp; size: u8,}impl Ball {&nbsp; &nbsp; fn update(&mut self, field: &Field) {}}struct Field {&nbsp; &nbsp; ball: Option<Ball>,}impl Field {&nbsp; &nbsp; fn update(&mut self) {&nbsp; &nbsp; &nbsp; &nbsp; if let Some(mut ball) = self.ball.take() {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ball.update(self);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; self.ball = Some(ball);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}解决方案:运行时检查您可以通过以下方式将借阅检查移至运行时而不是编译时RefCell:use std::cell::RefCell;struct Ball {&nbsp; &nbsp; size: u8,}impl Ball {&nbsp; &nbsp; fn update(&mut self, field: &Field) {}}struct Field {&nbsp; &nbsp; ball: RefCell<Ball>,}impl Field {&nbsp; &nbsp; fn update(&mut self) {&nbsp; &nbsp; &nbsp; &nbsp; self.ball.borrow_mut().update(self)&nbsp; &nbsp; }}
打开App,查看更多内容
随时随地看视频慕课网APP