借用检查器停止迭代器在递归功能中调用for_each

发布于 2025-02-02 04:50:04 字数 2363 浏览 4 评论 0原文

我正在Rust和Wasm上的一份扫雷副本。目前,我正在完成清洗矿井的逻辑。主要是,当用户单击一个空的瓷砖,附近没有炸弹时,该程序随后搜索邻居并揭示这些瓷砖,如果它们不在任何炸弹附近。

请注意,游戏中自动打开的大空格。

“清扫器的图像”

该程序使用两个1D向量存储炸弹所在的位置,而每个图块的状态是什么。

然后,此递归程序将发现的空瓷砖的状态设置为SpotState ::空,然后与邻居继续使用。

/// for uncovering empty neighbors and recursively uncovering their empty neighbors
fn uncover_empty_neighbors(&mut self, col: usize, row: usize) {
    // break case for recursion
    if self.state_vec[self.get_idx(col, row)] == SpotState::Empty {
        return;
    }

    if self.get_mine_neighbor_count(col, row) == 0 {
        let idx = self.get_idx(col, row);
        self.state_vec[idx] = SpotState::Empty;
    }

    let inbound_neighbors = POSSIBLE_NEIGHBORS
        .iter()
        .map(|[x, y]| [x + col as isize, y + row as isize])
        .filter(|[x, y]| self.check_bounds(x, y))
        .for_each(|[x, y]| self.uncover_empty_neighbors(x as usize, y as usize));
}

但是,我遇到了这个错误:

error[E0500]: closure requires unique access to `*self` but it is already borrowed
   --> src\lib.rs:138:23
    |
137 |             .filter(|[x, y]| self.check_bounds(x, y))
    |                     -------- ---- first borrow occurs due to use of `*self` in closure
    |                     |
    |                     borrow occurs here
138 |             .for_each(|[x, y]| {
    |              -------- ^^^^^^^^ closure construction occurs here
    |              |
    |              first borrow later used by call
139 |                 self.uncover_empty_neighbors(x as usize, y as usize)
    |                 ---- second borrow occurs due to use of `*self` in closure

For more information about this error, try `rustc --explain E0500`.
error: could not compile `minesweeper_wasm` due to previous error
warning: build failed, waiting for other jobs to finish...
error: build failed

我对如何实现这一目标感到困惑。闭合是否无法捕获self,因为self已经在函数undover_empty_neighbors()中借用了?在这种情况下,使用self不可避免地会出现不良的动作,哪些其他数据结构将有用?

链接到repo

I am working on a minesweeper copy in Rust and WASM. At the moment, I'm finishing the logic for Mine Sweeper. Mainly, when the user clicks on an empty tile with no bombs near it, the program then searches the neighbors and reveals those tiles if they are not near any bombs.

Notice the large empty spaces on the game that opens up automatically.

image of mine sweeper

The program uses two 1D vectors to store where the bombs are and also what is the state of each tile.

Then this recursive program sets the state of a found empty tile to SpotState::Empty, then continues this with the neighbors.

/// for uncovering empty neighbors and recursively uncovering their empty neighbors
fn uncover_empty_neighbors(&mut self, col: usize, row: usize) {
    // break case for recursion
    if self.state_vec[self.get_idx(col, row)] == SpotState::Empty {
        return;
    }

    if self.get_mine_neighbor_count(col, row) == 0 {
        let idx = self.get_idx(col, row);
        self.state_vec[idx] = SpotState::Empty;
    }

    let inbound_neighbors = POSSIBLE_NEIGHBORS
        .iter()
        .map(|[x, y]| [x + col as isize, y + row as isize])
        .filter(|[x, y]| self.check_bounds(x, y))
        .for_each(|[x, y]| self.uncover_empty_neighbors(x as usize, y as usize));
}

However, I get this error:

error[E0500]: closure requires unique access to `*self` but it is already borrowed
   --> src\lib.rs:138:23
    |
137 |             .filter(|[x, y]| self.check_bounds(x, y))
    |                     -------- ---- first borrow occurs due to use of `*self` in closure
    |                     |
    |                     borrow occurs here
138 |             .for_each(|[x, y]| {
    |              -------- ^^^^^^^^ closure construction occurs here
    |              |
    |              first borrow later used by call
139 |                 self.uncover_empty_neighbors(x as usize, y as usize)
    |                 ---- second borrow occurs due to use of `*self` in closure

For more information about this error, try `rustc --explain E0500`.
error: could not compile `minesweeper_wasm` due to previous error
warning: build failed, waiting for other jobs to finish...
error: build failed

I'm confused about how to accomplish this. Is the closure unable to capture self since self is already mutably borrowed in the function uncover_empty_neighbors()? Is using self inevitably a bad move in this case and what other data structure would be useful?

Link to repo

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

平安喜乐 2025-02-09 04:50:04

是的。请记住,对迭代器进行了懒惰的评估,因此将闭合传递到过滤器需要借用self and 继续借用它,而for_each正在执行,因为for_each需要评估调用它的迭代器。

您可以尝试重组代码,以便任何方法都不取决于self,也可以简单地collect()纳入新的vec filter()之后,因此没有不变的借入活着的借款为undover_empty_neighbors执行。

Yes. Remember that iterators are evaluated lazily, so the closure passed into filter needs to borrow self and keep borrowing it while for_each is executing, as for_each needs to evaluate the iterator it is called on.

You can either try to restructure the code so that either of the methods does not depend on self, or you can simply collect() into a new Vec after the filter(), so that there is no immutable borrow alive as uncover_empty_neighbors executes.

感性不性感 2025-02-09 04:50:04

您只需要尽可能避免借钱即可。在这种情况下,考虑宽度/高度的免费方法,考虑到它们是usize,是copy> copy> copy

/// for checking the bounds of a given row and col since neighbors are blindly checked.
fn check_bounds(col: &isize, row: &isize, width: usize, height: usize) -> bool {
    if col < &0 || row < &0 || col >= &(width as isize) || row >= &(height as isize) {
        return false;
    }
    true
}

/// for uncovering empty neighbors
fn uncover_empty_neighbors(&mut self, col: usize, row: usize) {
    // break case for recursion
    if self.state_vec[self.get_idx(col, row)] == SpotState::Empty {
        return;
    }

    if self.get_mine_neighbor_count(col, row) == 0 {
        let idx = self.get_idx(col, row);
        self.state_vec[idx] = SpotState::Empty;
    }
    let (width, height) = (self.width, self.height);
    let inbound_neighbors = POSSIBLE_NEIGHBORS
        .iter()
        .map(|[x, y]| [x + col as isize, y + row as isize])
        .filter(|[x, y]| Self::check_bounds(x, y, width, height))
        .for_each(|[x, y]| {
            self.uncover_empty_neighbors(x.clone() as usize, y.clone() as usize)
        });
}

“好吧,您正在用迭代器使用的封闭捕获self,因为迭代器懒惰,直到收集 borrow 才能释放(或者在您的情况下(在您的情况下,整个)评估for_each)。

You just need to try to avoid borrowing when possible. In this case making a free method taking an owned width/height is enough, considering that they are usize which is Copy:

/// for checking the bounds of a given row and col since neighbors are blindly checked.
fn check_bounds(col: &isize, row: &isize, width: usize, height: usize) -> bool {
    if col < &0 || row < &0 || col >= &(width as isize) || row >= &(height as isize) {
        return false;
    }
    true
}

/// for uncovering empty neighbors
fn uncover_empty_neighbors(&mut self, col: usize, row: usize) {
    // break case for recursion
    if self.state_vec[self.get_idx(col, row)] == SpotState::Empty {
        return;
    }

    if self.get_mine_neighbor_count(col, row) == 0 {
        let idx = self.get_idx(col, row);
        self.state_vec[idx] = SpotState::Empty;
    }
    let (width, height) = (self.width, self.height);
    let inbound_neighbors = POSSIBLE_NEIGHBORS
        .iter()
        .map(|[x, y]| [x + col as isize, y + row as isize])
        .filter(|[x, y]| Self::check_bounds(x, y, width, height))
        .for_each(|[x, y]| {
            self.uncover_empty_neighbors(x.clone() as usize, y.clone() as usize)
        });
}

Playground

What is going on? Well, you are capturing self withing the closures that the iterator use, since iterators are lazy that borrow is not release until you collect (or in your case the whole for_each is evaluated).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文