0

In my implementation of Conway's Game of Life in Rust, I've walked into a problem. I want to remember the previous state of the world. For this I've created an extra property on my Universe struct, which now looks like this:

pub struct Universe {
    width: usize,
    height: usize,
    cells: Vec<Cell>,
    previous_cells: Option<Vec<Cell>>,
}

The Option is there because at initialization, there is no previous state.

My idea would be that on every tick, we write the new state to the vector with the previous state. At the end, I swap the vectors. This resulted in the following code:

pub fn tick(&mut self) {
    // get the previous state or create new one if it doesn't exist (i.e. this is the first tick/update)
    let mut next = match self.previous_cells {
        Some(cells) => cells,
        None => {
            let mut new_vec = Vec::with_capacity(self.width() * self.height());
            new_vec.resize(new_vec.capacity(), Cell::Dead);
            new_vec
        }
    };

    // Calculate next state, not relevant for question
    for row in 0..self.height as isize {
        for column in 0..self.width as isize {
            let index = self.get_index(row , column);
            let target_cell = self.cells[index];
            let live_neighbors = self.live_neighbor_count(row, column);

            next[index] = match target_cell {
                Cell::Alive => if live_neighbors == 2 || live_neighbors == 3 { Cell::Alive } else { Cell::Dead }
                Cell::Dead => if live_neighbors == 3 { Cell::Alive } else { Cell::Dead }
            }
        }
    }

    // swap values
    self.previous_cells = Some(self.cells);
    self.cells = next;
}

Now I get the following errors from the compiler:

error[E0507]: cannot move out of `self.previous_cells.0` which is behind a mutable reference
  --> src/lib.rs:61:30
   |
61 |         let mut next = match self.previous_cells {
   |                              ^^^^^^^^^^^^^^^^^^^ help: consider borrowing here: `&self.previous_cells`
62 |             Some(cells) => cells,
   |                  -----
   |                  |
   |                  data moved here
   |                  move occurs because `cells` has type `std::vec::Vec<Cell>`, which does not implement the `Copy` trait

error[E0507]: cannot move out of `self.cells` which is behind a mutable reference
  --> src/lib.rs:83:36
   |
83 |         self.previous_cells = Some(self.cells);
   |                                    ^^^^^^^^^^ move occurs because `self.cells` has type `std::vec::Vec<Cell>`, which does not implement the `Copy` trait

I get why this happens, but I don't see a solution that does not include copying the entire Vec. That would be a solution, but is there maybe a better one?

Jesse Maas
  • 51
  • 6
  • Not really an answer to your question, but since you need to allocate a new vector _after_ the first step anyway, why don't you simply do it _before_ the first step? This does not reduce performance, since you need that allocation anyway, and you won't need to check in each iteration anymore. – Sven Marnach Mar 05 '20 at 16:51
  • 1
    This would also allow you to get rid of the `Option`, so swapping the two vectors becomes a trivial call to `std::mem::swap()`. – Sven Marnach Mar 05 '20 at 16:52
  • @SvenMarnach IMO that is a pretty good answer and shouldn't just be in the comments. If it turns out the `Option` is really needed, a `mem::swap` is still possible, just not as trivial. – jplatte Mar 05 '20 at 17:45
  • @jplatte With the option this is possible, but the code gets rather ugly. We would have to turn `next` into a mutable borrow of the inner value of the option, but we couldn't hold onto that borrow the whole time, since we couldn't call any other methods on `self` while holding that borrow. Overall, it would be best to completely change the structure here to more clearly separate concerns, but I don't have time to write this up. – Sven Marnach Mar 05 '20 at 21:00

0 Answers0