2015-06-21 103 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