存储闭包引用其他闭包

时间:2015-11-01 07:02:21

标签: closures rust

我正在尝试创建一个回调系统,其中任何回调都可以通过包含在另一个回调中来修改它在调用它之前或之后的行为。此代码有效:

type Closure = Box<Fn(&i32) -> i32>;

fn foo() {
    let add_closure = |closure: Closure| {
        let bar: Closure = Box::new(move |x| {
            println!("{}", x);
            closure(x)
        });
    };

    add_closure(Box::new(|&x| x + 2));
}

fn main() {
    foo()
}

但是只要我在闭包中添加生命周期边界,它就不会:

type Closure<'a> = Box<Fn(&'a i32) -> i32>;

fn foo<'a>() {
    let add_closure = |closure: Closure<'a>| {
        let bar: Closure<'a> = Box::new(move |x| {
            println!("{}", x);
            closure(x)
        });
    };

    add_closure(Box::new(|&x| x + 2));
}

fn main() {
    foo()
}

我收到此错误:

./vec_closure.rs:5:32: 8:11 error: the type `[closure@./vec_closure.rs:5:41: 8:10 closure:Box<core::ops::Fn(&'a i32) -> i32 + 'static>]` does not fulfill the required lifetime [E0477]
./vec_closure.rs:5         let bar: Closure<'a> = Box::new(move |x| {
./vec_closure.rs:6             println!("{}", x);
./vec_closure.rs:7             closure(x)
./vec_closure.rs:8         });
note: type must outlive the static lifetime
error: aborting due to previous error

似乎添加生命周期限制导致Box成为'static,但我不明白为什么会这样或如何避免它。

1 个答案:

答案 0 :(得分:5)

首先,你需要知道Box<T>有一个隐含的生命周期;如果你拼出它,那就是Box<T + 'static>。这样做的原因是Box只要你想要它就可以存活,所以你 你可能想要的。只有具有'static生命周期的类型才有资格。换句话说,这可以防止你装箱只有暂时有效的东西(就像&'a T仅对'a有效)。

所以Box<Fn(&'a i32) -> i32>实际上是Box<(Fn(&'a i32) -> i32) + 'static>。但是,由于'a,这是一个问题。你可能认为这是在说“闭包需要一些生命周期的指针”,但事实并非如此。它说封闭作为一个整体在某些生命周期中被参数化,因此,只有生命周期内才有效。

换句话说,不是说“这个类型是一个带有指针的闭包(对'a有效”),而是说“这种类型(对{{1有效”) }})是一个带有指针的闭包(也适用于'a)“。

这与'a类型的隐式'static不兼容,因此无效。

实际上想要的是使得闭包对任何旧生命有效,并且只是它的参数受到限制。您可以使用Higher-Rank Trait Bounds执行此操作,如下所示:

Box<T>

现在,不是选择type Closure = Box<for<'a> Fn(&'a i32) -> i32>; 类型有效的特定生命周期,而是说本身类型始终有效,但参数类型受限制通过一些任意的生命。

此时,您可以将Closure替换为Closure<'a>,然后就可以了。

Closure