我有一个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;
然而,Rust抱怨这个错误:
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
答案 0 :(得分:5)
你的情况需要多次冲突的可变借用,而rustc正在否定这一点。我们应该了解&amp;为什么会这样呢!
重要的说明:
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的相同副本(听起来不合适)。
您的第二个版本的代码基本上会遇到相同的限制。
如果您想要解决借阅的排他性问题,可以使用RefCell
或Mutex
等特殊容器来序列化对Rng的访问。