如何修改集合同时迭代它?

arn*_*rnm 6 collections iterator immutability rust

我有一个Board(aka &mut Vec<Vec<Cell>>)我想迭代它时更新.我想要更新的新值来自一个函数,它需要一个&Vec<Vec<Cell>>我正在更新的集合.

我尝试了几件事:

  1. 使用board.iter_mut().enumerate(),row.iter_mut().enumerate()以便我可以cell在最里面的循环中更新.Rust不允许调用该next_gen函数,因为它需要a,&Vec<Vec<Cell>>并且当您已经有一个可变引用时,您不能拥有不可变引用.

  2. 更改next_gen功能签名以接受a &mut Vec<Vec<Cell>>.Rust不允许对对象进行多次可变引用.

我目前正在将所有更新推迟到a HashMap,然后在我执行迭代后应用它们:

fn step(board: &mut Board) {
    let mut cells_to_update: HashMap<(usize, usize), Cell> = HashMap::new();
    for (row_index, row) in board.iter().enumerate() {
        for (column_index, cell) in row.iter().enumerate() {
            let cell_next = next_gen((row_index, column_index), &board);
            if *cell != cell_next {
                cells_to_update.insert((row_index, column_index), cell_next);
            }
        }
    }

    println!("To Update: {:?}", cells_to_update);
    for ((row_index, column_index), cell) in cells_to_update {
        board[row_index][column_index] = cell;
    }
}
Run Code Online (Sandbox Code Playgroud)

完整来源

有没有办法可以让这个代码更新board"就地",也就是说,在最里面的循环中,同时仍然可以next_gen在最里面的循环中调用?

免责声明:

我正在学习Rust,我知道这不是最好的方法.我正在玩,看看我能做什么,不能做什么.我也试图限制任何复制以限制自己一点点.正如oli_obk - ker提到的那样,Conway的生命游戏的这种实现是有缺陷的.

此代码旨在衡量以下几点:

  1. 如果这是可能的话
  2. 如果它是惯用的Rust

从我在评论中收集到的内容来看,它是可能的std::cell::Cell.但是,使用std:cell:Cell规避了一些核心Rust原则,我在原始问题中将其描述为我的"两难".

A.B*_*.B. 5

有没有办法让这个代码"就地"更新电路板?

存在专门为这些情况制作的类型.巧合地叫它std::cell::Cell.Cell即使多次不可挽回地借用,你也可以改变a的内容.Cell仅限于实现的类型Copy(对于您必须使用的其他类型,RefCell如果涉及多个线程,则必须Arc与somethinng组合使用,例如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);
}
Run Code Online (Sandbox Code Playgroud)

  • 问题不在于运行时成本,而是Rust借用检查规则的规避,它不仅有助于防止内存不安全,而且还可以防止由于在任意位置(而不是仅在何处修改同一内存位置)而导致的意外行为您有一个`&mut`参考。考虑一下它,就像C ++类成员上的`mutable`关键字一样。您的非可变对象突然被允许修改。 (2认同)

oli*_*obk 5

这完全取决于你的next_gen功能.假设除了签名之外我们对该函数一无所知,最简单的方法是使用索引:

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;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

有关next_gen不同解决方案的更多信息可能是可能的,但它听起来很像我的细胞自动机,据我所知,这不能在Rust中以迭代器方式完成而不改变类型Board.

您可能担心索引解决方案的效率低于迭代器解决方案,但您应该信任LLVM.如果您的next_gen函数在另一个包中,您应该将其标记为#[inline]LLVM也可以对其进行优化(如果所有内容都在一个包中,则不需要).


不是你的问题的答案,而是你的问题:

由于您正在实施康威的生命游戏,因此您无法就地进行修改.想象一下以下模式:

00000
00100
00100
00100
00000
Run Code Online (Sandbox Code Playgroud)

如果更新第2行,它会1将该行更改为a,0因为它1的邻域中只有两个s.这将导致中间1只看到两个1s而不是那里开始的三个.因此,您始终需要复制整个副本Board,或者像在代码中一样,将所有更改写入其他位置,并在遍历整个板后将其拼接.