2015-06-06 21 views
7

Sau đây là mô phỏng đơn giản với trường là khu vực hình chữ nhật có hai quả bóng nảy xung quanh trong đó. Cấu trúc Field có phương thức update, gọi là update trên mỗi quả bóng. Các quả bóng, theo phương pháp update của chúng, cần di chuyển xung quanh dựa trên vận tốc của chúng. Nhưng họ cũng cần phải phản ứng với nhau, cũng như ranh giới của lĩnh vực công trình .:Truyền tham chiếu tự biến đổi được tới phương thức của đối tượng sở hữu

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

Làm thế nào để tôi nhận được thông tin về ranh giới và bóng khác để chức năng cập nhật các Ball struct không? Những dòng trong Field::update không biên dịch:

self.balls[0].update(self); 
self.balls[1].update(self); 

Cho các lỗi sau:

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 

mà tôi hiểu, nhưng tôi không biết làm thế nào để làm được việc này.

Trả lời

6

Hiện tại, cấu trúc Ball của bạn cần biết về Field cấu trúc được chứa để có thể tự cập nhật. Điều này không biên dịch vì kết quả sẽ là các tham chiếu tuần hoàn kết hợp với đột biến. Bạn có thể thực hiện công việc này bằng cách sử dụng Cell hoặc RefCell (cái sau có chi phí hiệu suất) nhưng sẽ tốt hơn nếu cấu trúc mã khác nhau. Để cấu trúc Field kiểm tra và giải quyết Ball - BallBall - Wall va chạm. Chức năng Ball struct's update có thể xử lý việc cập nhật vị trí của 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

Yeah, mà sẽ làm việc . Man, Rust thực sự khiến tôi suy nghĩ khác về cách tôi lập trình. –

1

Dưới đây là một ví dụ nhỏ:

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

Vấn đề gốc rễ là khi bạn vượt qua trong một tham chiếu đến Field, bạn đang làm cho sự đảm bảo rằng các Field không thể thay đổi (các bất biến phần của "tham chiếu không thay đổi"). Tuy nhiên, bạn cũng đang cố gắng biến đổi một phần của nó: quả bóng! Tham chiếu nào nên có thẩm quyền, self hoặc field, khi triển khai Ball::update?

Một giải pháp là riêng biệt các bộ phận của cơ cấu cần thiết cho update và những người không và sử dụng chúng trước gọi update chức năng:

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

Bạn thậm chí có thể bó những tài liệu tham khảo trọn vẹn thành một gói gọn gàng :

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

Hoặc cấu trúc lại cấu trúc có chứa của bạn để tách thông tin ngay từ đầu:

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

Bạn cũng có thể đi theo cách khác và loại bỏ các Ball từ Field trước khi thực hiện bất kỳ thay đổi nó.Nếu bạn dễ dàng/giá rẻ có thể làm cho một Ball, hãy thử thay thế nó:

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

Nếu bạn không thể dễ dàng thực hiện, bạn có thể sử dụng một Optiontake nó:

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); 
     } 
    } 
} 
Các vấn đề liên quan