2015-06-21 67 views
4

Gist to source修改集合在遍历它

免责声明:我刚开始学习锈,我知道这是不是要做到这一点的最好办法。我只是在玩耍,看看我能做什么,不能做什么。我也试图限制任何复制以限制自己。

我有一个&mut Vec<Vec<Cell>> aka a Board我想更新它,同时迭代它。 我目前面临的困境是,我想要更新的新值来自一个函数,该函数需要一个&Vec<Vec<Cell>>来更新我的集合。

我试了几件事。第一个尝试使用board.iter_mut().enumerate()row.iter_mut().enumerate(),以便我可以更新内部最多循环中的cell,但Rust不允许调用next_gen函数,因为它需要&Vec<Vec<Cell>>,并且当您已经有可变引用时,您不能拥有不可变引用。

我也尝试将next_gen函数签名更改为接受&mut Vec<Vec<Cell>>,但Rust不允许对对象进行多次可变引用。

所以我的问题是:有没有办法,我能做出这样的代码更新“到位”的board,就是最内层循环,同时仍然能够调用next_gen最内层循环内内?

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; 
    } 
} 

更新1

因为它已被讨论,这种类型的实施康威生命游戏的是有缺陷的,由ker所强调的意见了。这只是为了衡量一些事情:1)如果这是可能的,2)如果它是惯用的Rust代码。根据我在评论中收集的内容,可以使用std::cell::Cell。但是,使用std:cell:Cell则避开了一些Rust原理的核心原则,我在原始问题中将其描述为我的“困境”。

回答

2

有没有一种方法可以让这段代码更新电路板“到位”?

存在这样的情况下特别制造的类型。它巧合地被称为std :: cell :: Cell。即使已经多次不断地借用,你也可以改变Cell的内容。 Cell仅限于实现Copy的类型(对于其他您必须使用RefCell的类型,并且如果涉及多个线程,则必须将Arc与Cell或RefCell结合使用)。

fn main() { 
    use std::cell::Cell; 
    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

我没有看到Cell是否是最后一招运行时成本。使用不同方法或使用Cell的选择归结为优先选择。 –

+1

这个问题不是运行时成本,它是Rust的借用检查规则的规避,它不仅有助于防止内存不安全,还可以防止修改相同内存位置而导致的意外行为,而不是仅仅在任意位置你有'&mut'引用。想想它有点像C++类成员上的'mutable'关键字。你的非可变对象突然被允许被修改。 –

3

这完全取决于你的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; 
      } 
     } 
    } 
} 

随着更多有关next_gen不同的解决方案是可能的。但是对我来说这听起来很像一个元胞自动机,并且据我所知,这不能在Rust中以迭代器的方式完成,而不改变Board的类型。

您可能担心索引解决方案的效率会低于迭代器解决方案,但您应该对此信任llvm。如果您的next_gen功能位于另一个箱子中,您应该将其标记为#[inline],这样llvm也可以对其进行优化(如果一切都在一个箱子中,则不需要)。


不回答你的问题,但问题的方法:

既然要实现康威生命游戏,你不能做就地修改。想象一下以下模式:

00000 
00100 
00100 
00100 
00000 

如果更新2号线,它将在该行改变10,因为它在它的附近只有两个1秒。这将导致中间1只看到两个1 s,而不是三个在那里开始。因此,您始终需要复制整个Board,或者如您在代码中所做的那样,将所有更改写入其他位置,并在完成整个董事会后将其拼接。

+0

是的,这将是最简单的解决方案,但我只是想看看用迭代器来做任何替代方法。顺便说一句,代码是[这里](https://gist.github.com/anonymous/ea4b440c9bf77f49592e#file-main-rs-L53) – arnm

+0

迭代器是不可能的,除非你创建自己的迭代器(可能是板类型)是专门为您的细胞更新功能定制的。在一个相关的说明中,除非你的更新函数只访问当前单元格右边和底部的单元格,否则你将会变得非常奇怪,而且绝对不是同步元胞自动机。生活的游戏将不可能通过这种方式来实现。 –

+0

是的,那肯定会成为问题。感谢那。我并没有过多关注实施康威生命游戏的实际问题,而是一直试图学习Rust。 – arnm