2016-11-29 76 views
7

我有麻烦给一个结构成员变量是另一种类型的引用。这里是我的结构和实现。引用元素在向量

struct Player<'a> { 
    current_cell: &'a Cell, 
} 

impl<'a> Player<'a> { 
    pub fn new(starting_cell: &'a Cell) -> Player<'a> { 
     Player { current_cell: starting_cell } 
    } 
} 

球员有当前Cell,他们是在参考这里是我的Game结构及其实现:

struct Game { 
    is_running: bool, 
    cells: Vec<Cell>, 
} 

impl Game { 
    pub fn new() -> Game { 
     let cells = construct_cells(); 
     let player = Player::new(cells[0]); 

     Game { 
      is_running: false, 
      cells: cells, 
     } 
    } 
} 

cellsCell矢量秒。当我创建游戏时,我在construct_cells()中创建了一个单元格矢量,然后在第一个单元格处启动播放器。我得到的错误是:

expected &Cell, found struct `Cell` 

我可以看到,我不是路过的参考,当我创建了Player,但如果我更改参数来&cells[0]然后骂我借用整个向量和然后在创建Game结构时尝试再次使用它。发生什么了?我该如何给玩家提供一个Cell

+0

你对引用(或指针)是什么有很好的理解,尤其是什么*悬挂引用*(或指针)是什么?如果你给朋友一张带有你的地址的纸条(123 Main St.),然后**你移动了**,会发生什么?如果你的朋友刚刚出现并开始与你曾经居住过的房子里的人谈话,那么这个房子的新主人会如何感觉到,就好像他们一样? – Shepmaster

+0

@Shepmaster我知道什么是悬挂指针。我想我只是没有意识到这个解决方案可以允许一个。那么我将如何获得相同的结果呢?我正在尝试做什么? – Dooskington

+1

*我只是没有意识到这个解决方案可以允许一个* - 这就是为什么Rust是一个美妙的语言,国际海事组织。当你将'cells'从局部变量移动到'Game'结构体时,你已经无效化了向量中的所有引用,比如你给'player'的引用。像C或C++这样的语言可以让你做到这一点,AFAIK,并让代码在运行时崩溃。很难说出你真正想做什么,因为“玩家”从不使用。 – Shepmaster

回答

10

尽管出现了,但存储在可变向量中的对象的引用是而不是安全。请记住,矢量可以增长;一旦矢量超过了它的容量,就通过分配一个更大的数组并将其内部的所有对象移动到新的位置来增长它。对向量中的对象的任何引用都将被留下,而Rust不会允许这种情况发生。 (另外,可以缩小或清除矢量,在这种情况下,对其元素的任何引用都将指向释放内存。)C++ std::vector也存在同样的问题。

有几种方法。一个是从直接引用切换到Cell到安全反向引用,它由一个背指针Game和索引的向量元素的:

struct Player<'a> { 
    game: &'a Game, 
    cell_idx: usize, 
} 

impl<'a> Player<'a> { 
    pub fn new(game: &'a Game, cell_idx: usize) -> Player<'a> { 
     Player { 
      game: game, 
      cell_idx: cell_idx, 
     } 
    } 
    pub fn current_cell_name(&self) -> &str { 
     &self.game.cells[self.cell_idx].name 
    } 
} 

一个完整的编译的例子是at the playground

上面可能看起来像作弊 - 毕竟,Rust是一个系统语言,它具有引用,并且使用索引有点像cop-out。我们可以做得更好吗?

另一种选择是投入一点额外的努力,以确保Cell对象不受矢量重新分配的影响。在C++中一个将实现,通过使用指针至细胞,而不是细胞的矢量的矢量,和在锈病我们可以通过使用做同样的:

struct Game { 
    is_running: bool, 
    cells: Vec<Box<Cell>>, 
} 

impl Game { 
    fn construct_cells() -> Vec<Box<Cell>> { 
     ["a", "b", "c"].iter() 
      .map(|n| Box::new(Cell { name: n.to_string() })) 
      .collect() 
    } 

    pub fn new() -> Game { 
     let cells = Game::construct_cells(); 

     Game { 
      is_running: false, 
      cells: cells, 
     } 
    } 
} 

在的成本对于每个附加的Cell分配(和从Game额外指针引用到每个单元),这允许最大限度高效实现Player

struct Player<'a> { 
    cell: &'a Cell, 
} 

impl<'a> Player<'a> { 
    pub fn new(cell: &'a Cell) -> Player<'a> { 
     Player { 
      cell: cell, 
     } 
    } 
    pub fn current_cell_name(&self) -> &str { 
     &self.cell.name 
    } 
} 

再次,全编译示例at the playground