为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Git
慕妹3146593 2019-12-06 15:51:21
以下是一个简单的模拟,其中的场是一个矩形区域,其中有两个弹跳的球。该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我了解,但是我不知道该如何解决。
查看完整描述

2 回答

?
慕尼黑的夜晚无繁华

TA贡献1864条经验 获得超6个赞

当前,您的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

}


查看完整回答
反对 回复 2019-12-06
?
智慧大石

TA贡献1946条经验 获得超3个赞

这是一个较小的示例:


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不能更改(“不可变引用” 的不可变部分)。但是,此代码也试图改变它的一部分:球!self或field在实施中应参考哪个参考Ball::update?


解决方案:仅使用您需要的字段

您可以将结构所需的部分与不需要的部分分开,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

您也可以采用其他方法,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;

    }

}

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

    }

}


查看完整回答
反对 回复 2019-12-06
  • 2 回答
  • 0 关注
  • 372 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信