2016-11-29 75 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