Search code examples
rustforeachiteratorwebassemblyborrow-checker

Borrow checker stops iterator from calling for_each inside a recursive function


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


Solution

  • 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.