对向量中元素的引用 [英] Reference to element in vector

查看:54
本文介绍了对向量中元素的引用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我无法为结构提供一个成员变量,该变量是对另一种类型的引用.这是我的结构和实现:

I'm having trouble giving a struct a member variable that is a reference to another type. Here is my struct and implementation:

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 结构及其实现:

The player has a reference to the current Cell that they are in. Here is my Game struct and its implementation:

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() 中创建了一个单元格向量,然后从第一个单元格开始播放器.我得到的错误是:

cells is a vector of Cells. When I create the game, I create a vector of cells in construct_cells() and then I start the player at the first cell. The error I am getting is:

expected &Cell, found struct `Cell`

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

I can see that I'm not passing a reference when I create the Player, but if I change the parameter to &cells[0] then it yells at me for borrowing the entire vector and then trying to use it again when I create the Game struct. So what's going on? How do I just give the player a reference to a Cell?

推荐答案

尽管如此,存储对存储在可变向量中的对象的引用是安全的.载体可以增长;一旦向量的长度与其容量相匹配,它只能通过分配更大的数组并将其中的所有对象移动到新位置来增长.对其元素的现有引用将被悬空,因此 Rust 不允许这样做.(此外,向量可以缩小或清除,在这种情况下,对其元素的任何引用显然都指向已释放的内存.)C++ std::vector 也会存在同样的问题.

Despite appearances, storing the reference to an object stored in a mutable vector is not safe. Vectors can grow; once a vector's length matches its capacity, it can only grow by allocating a larger array and moving all objects inside it to the new location. Existing references to its elements would be left dangling, so Rust doesn't allow that. (Also, a vector can be shrunk or cleared, in which case any references to its elements will obviously point to deallocated memory.) The same problem would exist with a C++ std::vector.

有几种方法可以解决.一种是从对 Cell 的直接引用切换到安全的反向引用,该反向引用由指向 Game 的反向指针和指向向量元素的索引组成:

There are several ways around it. One is to switch from a direct reference to Cell to a safe back-reference that consists of a back-pointer to the Game and an index to the vector element:

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/a>.

Compilable example at the playground.

它的缺点是不允许添加单元格,除非附加它们,因为它会使玩家的索引无效.它还需要对 Player 对单元格属性的每次访问进行边界检查.但 Rust 是一种具有引用和智能指针的系统语言——我们能做得更好吗?

That has the downside that it doesn't allow adding cells except by appending them, because it would invalidate players' indices. It also requires bounds-checking on every access to a cell property by Player. But Rust is a systems language that has references and smart pointers - can we do better?

另一种方法是投入一些额外的努力来确保 Cell 对象不受向量重新分配的影响.在 C++ 中,可以通过使用指向单元格的指针向量而不是单元格向量来实现这一点,而在 Rust 中,可以通过将 Box 存储在向量中来实现这一点.但这不足以满足借用检查器的要求,因为缩小或删除向量仍然会使单元格无效.

The alternative is to invest a bit of additional effort to make sure that Cell objects aren't affected by vector reallocations. In C++ one would achieve that by using a vector of pointers-to-cell instead of a vector of cells, which in Rust one would use by storing Box<Cell> in the vector. But that wouldn't be enough to satisfy the borrow checker because shrinking or dropping the vector would still invalidate the cells.

这可以使用引用计数指针来修复,这将允许单元格在向量增长(因为它被分配在堆上)和收缩时幸存下来,因此它不再包含它(因为它不是由向量):

This can be fixed using a reference-counted pointer, which will allow the cell to both survive the vector growing (because it is allocated on the heap) and shrinking so it no longer inclues it (because it is not owned exclusively by the vector):

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 比索引更高效:

At the cost of an additional allocation for each Cell (and an additional pointer dereference from Game to each cell), this allows an implementation of Player more efficient than index:

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

再次,可编译示例 游乐场.

这篇关于对向量中元素的引用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆