2015-06-06 55 views
7

以下是一個簡單的模擬,其中一個場是一個矩形區域,其中有兩個球在其中彈跳。 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 

我理解,但我不知道怎麼去解決這個問題。

回答

6

當前您的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 
} 
+1

是啊,這將工作。男人,魯斯特真的讓我對我的計劃方式有不同的想法。 –

1

這裏有一個小例子:

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不能改變(的不變部分「不變的參考」)。然而,你也試圖改變它的一部分:球!哪一個參考應該是權威的,selffield,在執行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) 
    } 
} 

你也可以去其他方式在進行任何更改之前,請從FieldBall。如果你可以很容易地/廉價地製作Ball,嘗試更換:

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