Michael Francis
Michael Francis

Reputation: 8757

How can I match without borrowing in Rust?

I'm wrestling with the borrow checker while trying to write Conway's Game of Life. I have two for loops doing a mutable borrow on self.cell_states, which is a Vec<Vec<CellState>> (CellState is an enum) so that I can update the Alive or Dead status of each cell.

In order to decide if a cell should be alive or dead though, it needs to see how many of the surrounding cells are alive. This is where I've run into problems. The only way I can find to check if a cell is alive is to do a match statement, but apparently match borrows the value, which is not allowed, since I've already done a mutable borrow. It seems to me that I could just copy the value and check against the copied value, so I tried match self.cell_states[i+x-1][j+y-1].clone() {...}, but to no avail. How can I do a match without borrowing?

error[E0502]: cannot borrow `cell_states` as immutable because it is also borrowed as mutable
  --> src/main.rs:18:27
   |
11 |     for (i, row) in cell_states.iter_mut().enumerate() {
   |                     ----------------------------------
   |                     |
   |                     mutable borrow occurs here
   |                     mutable borrow used here, in later iteration of loop
...
18 |                     match cell_states[i+x-1][j+y-1] {
   |                           ^^^^^^^^^^^ immutable borrow occurs here

My code is as follows (Playground):

#[derive(Copy, Clone)]
pub enum CellState {
    Dead,
    Alive
}

fn main() {
    let mut cell_states = vec![vec![CellState::Dead; 10]; 10];


    for (i, row) in cell_states.iter_mut().enumerate() {
        for (j, cell) in row.iter_mut().enumerate() {
            // Count the living cells around cell
            let mut count = 0;

            for x in 0..3 {
                for y in 0..3 {
                    match cell_states[i+x-1][j+y-1] {
                        CellState::Alive => count += 1,
                        _ => ()
                    }
                }
            }
        }
    }
}

Upvotes: 3

Views: 1495

Answers (1)

Lukas Kalbertodt
Lukas Kalbertodt

Reputation: 89016

How can I match without borrowing in Rust?

You can't.

Every time you access a variable, you are borrowing it. Printing a variable? That's borrowing. Checking if the variable has a specific value? Borrowing again. Copying the value of a variable? Also borrowing.

You are trying to access values of the vector while the vector is mutably borrowed. This just won't work -- the borrow checker forbids this.

Can you work around this? Yes! The usual trick when working with vectors or arrays is to delay all borrows by iterating over the indices instead. So write something like this:

for i in 0..cell_states.len() {
    for j in 0..cell_states[i].len() {
        ...
    }
}

The vector is not borrowed in that case, but you can borrow elements of it later by just indexing it.

But: the borrow checker just saved you, because what you attempted was buggy! What you tried is not how Game of Life works! You need two grids of cells, because the update step cannot update all cells on the fly. You first have to calculate all new cells into a new vector (so that you can use all the old cell values while calculating the new ones) and then you can replace the old vector with the new one. So the update step in Game of Life is a two step process -- you cannot do it in-place.

And after fixing that real bug, you will find that you won't run into this specific borrow checker problem again.

Upvotes: 6

Related Questions