参考向量中的元素

Doo*_*ton 10 rust

我在给一个struct一个成员变量时遇到了麻烦,该变量是对另一个类型的引用.这是我的结构和实现:

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 }
    }
}
Run Code Online (Sandbox Code Playgroud)

玩家可以参考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,
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

cellsCells 的向量.当我创建游戏时,我创建了一个单元格向量,construct_cells()然后我在第一个单元格中启动了玩家.我得到的错误是:

expected &Cell, found struct `Cell`
Run Code Online (Sandbox Code Playgroud)

我可以看到我在创建时没有传递引用Player,但是如果我将参数更改为&cells[0]然后它会向我大吼大叫借用整个向量然后在创建Game结构时再次尝试使用它.发生什么了?我如何只给玩家一个参考Cell

use*_*342 19

尽管出现,存储所述参考存储在一个可变的矢量,其目的在于安全的.请记住,载体可以生长; 一旦向量超过其容量,它就会通过分配一个更大的数组并将其中的所有对象移动到新位置来增长.对向量中对象的任何引用都将保持悬空状态,并且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
    }
}
Run Code Online (Sandbox Code Playgroud)

一个完整的可编辑的例子是在操场上.

上面的内容可能看起来像是作弊 - 毕竟,Rust是一种具有引用的系统语言,使用索引在某种程度上是一种伪造.我们可以做得更好吗?

另一种选择是投入一些额外的努力来确保Cell对象不受向量重新分配的影响.在C++中,可以通过使用指针到单元格的向量而不是单元格的向量来实现这一点,在Rust中我们可以通过使用来完成相同的操作:

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,
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

以每个附加分配为代价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
    }
}
Run Code Online (Sandbox Code Playgroud)

再次,在操场上完整的可编辑示例.

  • 一件事对我来说不是很明显:存储对向量项的“真实”(不是基于索引的)引用需要借用整个向量,从而无法改变向量(例如清除它从而使引用无效)或它的任何项目。 (4认同)