Collections 如何在对集合进行迭代的同时修改集合?
我有一个Collections 如何在对集合进行迭代的同时修改集合?,collections,iterator,rust,immutability,Collections,Iterator,Rust,Immutability,我有一个板(又称&mut-Vec),我想在迭代时更新它。我要更新的新值是从一个函数派生的,该函数需要对我要更新的集合使用&Vec 我试过几种方法: 使用board.iter_mut().enumerate()和row.iter_mut().enumerate(),这样我就可以更新最内层循环中的单元格。Rust不允许调用next\u gen函数,因为它需要&Vec,并且当您已经有一个可变引用时,您不能有一个不变的引用 将下一代函数签名更改为接受&mut-Vec。Rust不允许对一个对象进行多个可变
板
(又称&mut-Vec
),我想在迭代时更新它。我要更新的新值是从一个函数派生的,该函数需要对我要更新的集合使用&Vec
我试过几种方法:
board.iter_mut().enumerate()
和row.iter_mut().enumerate()
,这样我就可以更新最内层循环中的单元格。Rust不允许调用next\u gen
函数,因为它需要&Vec
,并且当您已经有一个可变引用时,您不能有一个不变的引用
下一代
函数签名更改为接受&mut-Vec
。Rust不允许对一个对象进行多个可变引用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;
}
}
fn步骤(板:&mut板){
让mut cells_to_更新:HashMap=HashMap::new();
for board.iter()枚举()中的(行索引,行){
for行中的(列索引,单元格)。iter()枚举(){
让cell_next=next_gen((行索引、列索引)和board);
如果*单元格!=下一个单元格{
单元格到更新。插入((行索引,列索引),单元格到下一个);
}
}
}
println!(“要更新:{:?}”,单元格要更新);
对于单元格中的((行索引,列索引),单元格)更新为{
board[行索引][列索引]=单元格;
}
}
是否有一种方法可以使此代码更新板
“就地”,即在最内层循环内,同时仍能在最内层循环内调用下一代
免责声明:
我正在学习生锈,我知道这不是最好的方法。我在到处玩,看看我能做什么和不能做什么。我还试图限制任何复制,以限制自己一点。作为
此代码旨在衡量以下几点:
std::cell::cell
。然而,使用std:cell:cell
规避了一些核心生锈原则,我在最初的问题中将其描述为我的“困境”
有没有办法让我的代码“就地”更新董事会
有一种是专门为这种情况设计的。它恰巧被称为。您可以对单元格的内容进行变异,即使该单元格已被永久借用多次<代码>单元格
仅限于实现复制
的类型(对于其他类型,您必须使用,如果涉及多个线程,则必须将a与a等组合使用)
这完全取决于您的下一代功能。假设我们除了对函数的签名一无所知,最简单的方法是使用索引:
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;
}
}
}
}
有了更多关于下一代的信息,可能会有一个不同的解决方案,但对我来说,它听起来很像细胞自动机,据我所知,如果不改变电路板的类型,就无法在Rust中以迭代器的方式完成
您可能担心索引解决方案的效率会低于迭代器解决方案,但在这方面您应该信任LLVM。如果您的next_gen
函数在另一个板条箱中,您应该将其标记为#[inline]
,以便LLVM也可以对其进行优化(如果所有内容都在一个板条箱中,则不必如此)
不是对你问题的回答,而是对你问题的回答:
由于您正在实施康威的生活游戏,因此无法进行适当的修改。设想以下模式:
00000
00100
00100
00100
00000
如果更新第2行,它会将该行中的1
更改为0
,因为它的邻域中只有两个1
s。这将导致中间的1
只看到两个1
s,而不是原来的三个。因此,您始终需要复制整个电路板
,或者像在代码中那样,将所有更改写入其他位置,并在浏览整个电路板后将其拼接。如文档所述,内部可变性是最后的手段,我相信在回到单元类型之前,还有其他的途径可以探索。我不明白在没有运行时成本的情况下,单元如何成为最后的手段。选择使用不同的方法还是使用单元格取决于偏好。问题不在于运行时成本,而是规避了Rust的借用检查规则,这不仅有助于防止内存不安全,而且还可以防止由于在任意位置修改同一内存位置而导致的意外行为,而不仅仅是在有&mut
引用的位置。想想C++类成员的<代码>可更改的关键字。你的不可变对象突然被允许被修改。我同意一个单元格不应该公开给用户——但作为抽象的构建块,它是非常好的。1)它允许多次写入的逻辑错误,这可能是不需要的2)近似值的成本仅为零,实际上它也不会/可能不会优化。当然,这是最简单的解决方案,但我只是想看看用迭代器实现它的其他方法。顺便说一句,代码是,除非您创建自己的迭代器(可能是板类型的迭代器),该迭代器是专门为您的单元更新函数定制的,否则使用迭代器是不可能的。另一方面,除非update函数只访问当前单元格右侧和底部的单元格,否则会出现非常奇怪的行为,而且肯定不是syn
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;
}
}
}
}