How can I modify a collection while also iterating over it?

前端 未结 2 1169
抹茶落季
抹茶落季 2020-12-06 12:50

I have a Board (a.k.a. &mut Vec>) which I would like to update while iterating over it. The new value I want to update wi

相关标签:
2条回答
  • 2020-12-06 13:01

    It entirely depends on your next_gen function. Assuming we know nothing about the function except its signature, the easiest way is to use indices:

    fn step(board: &mut Board) {
        for row_index in 0..board.len() {
            for column_index in 0..board[row_index].len() {
                let cell_next = next_gen((row_index, column_index), &board);
                if board[row_index][column_index] != cell_next {
                    board[row_index][column_index] = cell_next;
                }
            }
        }
    }
    

    With more information about next_gen a different solution might be possible, but it sounds a lot like a cellular automaton to me, and to the best of my knowledge this cannot be done in an iterator-way in Rust without changing the type of Board.

    You might fear that the indexing solution will be less efficient than an iterator solution, but you should trust LLVM on this. In case your next_gen function is in another crate, you should mark it #[inline] so LLVM can optimize it too (not necessary if everything is in one crate).


    Not an answer to your question, but to your problem:

    Since you are implementing Conway's Game of Life, you cannot do the modification in-place. Imagine the following pattern:

    00000
    00100
    00100
    00100
    00000
    

    If you update line 2, it will change the 1 in that line to a 0 since it has only two 1s in its neighborhood. This will cause the middle 1 to see only two 1s instead of the three that were there to begin with. Therefor you always need to either make a copy of the entire Board, or, as you did in your code, write all the changes to some other location, and splice them in after going through the entire board.

    0 讨论(0)
  • 2020-12-06 13:25

    Is there a way that I could make this code update the board "in place"?

    There exists a type specially made for situations such as these. It's coincidentally called std::cell::Cell. You're allowed to mutate the contents of a Cell even when it has been immutably borrowed multiple times. Cell is limited to types that implement Copy (for others you have to use RefCell, and if multiple threads are involved then you must use an Arc in combination with somethinng like a Mutex).

    use std::cell::Cell;
    
    fn main() {
        let board = vec![Cell::new(0), Cell::new(1), Cell::new(2)];
    
        for a in board.iter() {
            for b in board.iter() {
                a.set(a.get() + b.get());
            }
        }
        println!("{:?}", board);
    }
    
    0 讨论(0)
提交回复
热议问题