我有一个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));
Run Code Online (Sandbox Code Playgroud)
这里,闭包创建一个包装PRNG生成的值的迭代器.这个迭代器包含一个带有闭包的映射,其中包含x克隆范围,但问题是它无意中克隆rng了,我已经验证过了.有必要使它成为一个移动闭包,因为x必须捕获值,否则闭包将比活动更长x.
我试图添加此行以强制它将引用移动到闭包中:
let rng = &mut rng;
Run Code Online (Sandbox Code Playgroud)
但是,Rust抱怨这个错误:
error: cannot move out of captured outer variable in an `FnMut` closure
Run Code Online (Sandbox Code Playgroud)
我可以从移动闭包内部可变地访问PRNG,如果没有,因为PRNG明显超过了函数调用,是否有替代解决方案(除了重新设计API)?
编辑:
我已经重写它以删除副本问题,并且调用如下所示:
someFunction(|x| rng.gen_iter::<usize>().map(move |y| y % x).take(3));
Run Code Online (Sandbox Code Playgroud)
这会导致新的错误:
error: cannot infer an appropriate lifetime for autoref due to conflicting requirements
Run Code Online (Sandbox Code Playgroud)
你的情况需要多次冲突的可变借用,而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)
});
}
Run Code Online (Sandbox Code Playgroud)
让我把这点放在以下几点:
someFunction想要一个可以调用的闭包,每次调用它都会返回一个迭代器.闭包是可变的,可以多次调用(FnMut).
我们必须假设所有返回的迭代器可以同时使用,而不是按顺序(一次一个).
我们想借用Rng进入迭代器,但可变借用是独占的.因此,借用规则一次不允许多个迭代器.
FnOnce而不是FnMut将成为帮助我们的闭包协议的一个例子.它会使rustc看到只有一个迭代器.
在工作版本中,没有行(##),你有几个迭代器同时处于活动状态,那里发生了什么?这是隐式复制踢,所以每个迭代器将使用原始Rng的相同副本(听起来不合需要).
您的第二个版本的代码遇到了基本相同的限制.
如果您想解决借用的排他性问题,您可以使用特殊容器RefCell或Mutex序列化对Rng的访问.
| 归档时间: |
|
| 查看次数: |
867 次 |
| 最近记录: |