如何捕获从闭包

时间:2015-11-02 16:50:48

标签: closures rust

我有一个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

1 个答案:

答案 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的相同副本(听起来不合适)。

您的第二个版本的代码基本上会遇到相同的限制。

如果您想要解决借阅的排他性问题,可以使用RefCellMutex等特殊容器来序列化对Rng的访问。