2015-11-02 28 views
2

我有一個PRNG,我想允許閉包通過可變引用訪問。一切的壽命理論上可以工作了,這裏是什麼樣子:如何捕獲包含在迭代器中的可移動閉包從閉包中返回

fn someFunction<F, I>(mut crossover_point_iter_generator: F) 
     where F: FnMut(usize) -> I, I: Iterator<Item=usize>; 

let mut rng = Isaac64Rng::from_seed(&[1, 2, 3, 4]); 
someFunction(|x| (0..3).map(move |_| rng.gen::<usize>() % x)); 

這裏,關閉是創建一個包裝PRNG生成的值迭代器。這個迭代器包含一個閉包的映射,其中包含的包裝範圍爲x,但問題在於它無意中也克隆了rng,這也是我已驗證的。必須將其作爲移動關閉,因爲必須獲取x的值,否則關閉將超過x

我試圖加入這一行強制其參考移動到閉合:

let rng = &mut rng; 

然而,鏽抱怨此錯誤:

error: cannot move out of captured outer variable in an `FnMut` closure 

能否性情不定地從訪問PRNG在移動閉包內部,如果不是,由於PRNG顯然超過了函數調用,是否有替代解決方案(除了重新設計API)?

編輯:

我已經重寫它來刪除複製問題和呼叫看起來是這樣的:

someFunction(|x| rng.gen_iter::<usize>().map(move |y| y % x).take(3)); 

這將導致一個新的錯誤:

error: cannot infer an appropriate lifetime for autoref due to conflicting requirements 
+1

附帶的,但[**不使用**模取得範圍內的隨機數](http://stackoverflow.com/questions/10984974/why-do-people-say-there-is-modulo-偏置時 - 使用 - 一個隨機數發生器)!對於這種情況,「rand」箱子具有「在一定範圍內隨機」功能。 – Shepmaster

+1

@Shepmaster啊,謝謝。我對偏見問題很熟悉,但我沒有意識到Rng公開了一個界面來輕鬆處理這個問題!最好查看我正在使用的特徵的所有功能。我將假定它是[rand :: Rng :: gen_range()](https://doc.rust-lang.org/num/rand/trait.Rng.html#method.gen_range)? – vadix

+0

是的,我懶得找到^ _ ^的功能。還有[長版本](https://doc.rust-lang.org/num/rand/distributions/range/struct.Range.html),但是'gen_range'更容易理解。 – Shepmaster

回答

5

你得到了一個需要多次衝突的可變借款的情況,而rustc正在否認這種情況。這只是我們瞭解如何&爲什麼會發生這種情況!

的說明,這將是很重要的:

  • Isaac64Rng實現Copy,這意味着它隱含的副本,而不是僅僅移動。我假設是一個傳統/向後兼容的東西。

我寫了這個版本的代碼直得到它:

extern crate rand; 

use rand::Isaac64Rng; 
use rand::{Rng, SeedableRng}; 

fn someFunction<F, I>(crossover_point_iter_generator: F) 
    where F: FnMut(usize) -> I, I: Iterator<Item=usize> 
{ 
    panic!() 
} 

fn main() { 
    let mut rng = Isaac64Rng::from_seed(&[1, 2, 3, 4]); 
    let rng = &mut rng; /* (##) Rust does not allow. */ 
    someFunction(|x| { 
     (0..3).map(move |_| rng.gen::<usize>() % x) 
    }); 
} 

讓我把這個點:

  • someFunction想要一個封閉它可以調用,即返回迭代器每次被調用。封閉是可變的,可以多次調用(FnMut)。

  • 我們必須假設所有返回的迭代器可以同時使用,而不是按順序(一次一個)使用。

  • 我們想借用Rng進入迭代器,但可變的借入是獨佔的。所以借用規則一次不允許有多個迭代器。

  • FnOnce而不是FnMut將是關閉協議的一個例子來幫助我們在這裏。這會讓rustc看到只能有一個迭代器。

在工作版本,沒有線(##),你有幾個迭代器在同一時間激活,發生了什麼呢?這是隱式複製踢入,所以每個迭代器將使用原始Rng的相同副本(聽起來不合意)。

您的代碼的第二個版本運行到基本相同的限制。

如果您想要解決借貸的排他性問題,您可以使用特殊容器(如RefCellMutex)來序列化對Rng的訪問。

+2

*假設所有返回的迭代器可以同時使用* - +1。這是恕我直言,學習在未來幫助調試這種事情的關鍵。 – Shepmaster

+0

@Shepmaster謝謝你強調這一點。這是我誤解的地方。幸運的是把它改成FnOnce是令人滿意的。 – vadix