我无法为结构提供一个成员变量,该成员变量是对另一种类型的引用。这是我的结构和实现:
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,
}
}
}
cells
是一个由Cell
组成的向量。当我创建游戏时,我在 construct_cells()
中创建了一个单元格向量,然后我在第一个单元格处启动播放器。我得到的错误是:
expected &Cell, found struct `Cell`
我可以看到我在创建 Player
时没有传递引用,但是如果我将参数更改为 &cells[0]
然后它就会对我大喊大叫借用整个向量,然后在创建 Game
结构时尝试再次使用它。发生什么了?我如何仅向玩家提供对 Cell
的引用?
最佳答案
尽管表面上看,存储对存储在可变向量中的对象的引用是不安全的。向量可以增长;一旦向量的长度与其容量匹配,它只能通过分配更大的数组并将其中的所有对象移动到新位置来增长。对其元素的现有引用将悬空,因此 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, cell_idx }
}
pub fn current_cell_name(&self) -> &str {
&self.game.cells[self.cell_idx].name
}
}
可编译示例 at the playground .
它的缺点是它不允许添加单元格,除非附加它们,因为它会使玩家的索引无效。它还需要通过 Player
对单元格属性的每次访问进行边界检查。 .但是 Rust 是一种具有引用和智能指针的系统语言 - 我们可以做得更好吗?
另一种方法是投入一些额外的努力来确保 Cell
对象不受向量重新分配的影响。在 C++ 中,可以通过使用指向单元格的指针向量而不是单元格向量来实现这一点,在 Rust 中,可以通过存储 Box<Cell>
来实现。在向量中。但这不足以满足借用检查器的要求,因为缩小或删除矢量仍会使单元格无效。
这可以使用引用计数的指针来修复,这将允许单元格在向量增长(因为它是在堆上分配的)和收缩时存活下来,因此它不再包含它(因为它不被独占拥有)向量):
struct Game {
is_running: bool,
cells: Vec<Rc<Cell>>,
}
impl Game {
fn construct_cells() -> Vec<Rc<Cell>> {
["a", "b", "c"]
.iter()
.map(|n| {
Rc::new(Cell {
name: n.to_string(),
})
})
.collect()
}
pub fn new() -> Game {
let cells = Game::construct_cells();
Game {
is_running: false,
cells,
}
}
// we could also have methods that remove cells, etc.
fn add_cell(&mut self, cell: Cell) {
self.cells.push(Rc::new(cell));
}
}
以每个 Cell
的额外分配为代价(以及从 Game
到每个单元格的附加指针取消引用),这允许实现 Player
比索引更有效:
struct Player {
cell: Rc<Cell>,
}
impl Player {
pub fn new(cell: &Rc<Cell>) -> Player {
Player {
cell: Rc::clone(cell),
}
}
pub fn current_cell_name(&self) -> &str {
&self.cell.name
}
}
同样,可编译示例 at the playground .
关于rust - 引用向量中的元素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40875152/