【问题标题】:Store closure which references other closure存储引用其他闭包的闭包
【发布时间】:2015-11-01 07:02:20
【问题描述】:

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

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,但我不明白为什么会这样或如何避免它。

【问题讨论】:

    标签: closures rust


    【解决方案1】:

    首先,你需要知道Box&lt;T&gt;里面有一个隐含的生命周期;如果你把它拼出来,那就是Box&lt;T + 'static&gt;。这样做的原因是 Box 可以随心所欲地存在,所以你存储在它里面的东西需要和你一样长可能想要。只有具有'static 生命周期的类型才有资格。换句话说,这可以防止您对仅暂时有效的内容进行装箱(例如 &amp;'a T 仅对 'a 有效)。

    所以Box&lt;Fn(&amp;'a i32) -&gt; i32&gt; 实际上是Box&lt;(Fn(&amp;'a i32) -&gt; i32) + 'static&gt;。但是,这是一个问题,因为'a。您可能相信这是在说“闭包需要一些生命周期的指针”,但事实并非如此。这是说闭包作为一个整体在某个生命周期内被参数化,因此,仅在该生命周期有效。

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

    这与隐含的'static 不兼容,后者是Box&lt;T&gt; 类型的一部分,因此它不起作用。

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

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

    现在,不是选择Closure 类型有效的特定生命周期,而是说类型本身 始终有效,但参数的类型受某些限制任意生命周期。

    此时,您可以将Closure&lt;'a&gt; 替换为Closure,就可以了。

    type Closure = Box<for<'a> Fn(&'a i32) -> i32>;
    
    fn foo<'a>() {
        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()
    }
    

    【讨论】:

    • 如果我想在闭包的参数上指定一个特定的生命周期,有没有办法做到这一点?我使用它的方式是我有两个参数,其中一个与创建闭包的函数的一个参数具有相同的生命周期,另一个具有相同的生命周期参数。跨度>
    • @BenjaminLee 我不确定你在问什么。
    • 例如,fn foo&lt;'a&gt;(x: &amp;'a i32, closure: Closure&lt;'a&gt;) -&gt; i32 { closure(x) }。我要做的是确保闭包的参数与函数的生命周期参数之一具有相同的生命周期。我刚刚遇到的另一个问题是使用type Closure = Box&lt;for&lt;'a&gt; Fn(&amp;'a i32, Foo&lt;'a&gt;)&gt;,其中Foo 是一个结构。它可以工作,但我实际定义的任何闭包都表示它不适合 Closure 类型。
    • @BenjaminLee 但是为什么闭包需要有相同的生命周期?通过参数化参数的生命周期,它们将不再需要匹配。此外,您的描述对于您实际要完成的工作非常模糊。我认为您应该提出一个新问题,其中包含更多详细信息,以显示您正在尝试做什么。评论不是来回确定您要询问的内容的地方。
    猜你喜欢
    • 2015-09-27
    • 2014-11-15
    • 1970-01-01
    • 2014-06-25
    • 1970-01-01
    • 2020-06-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多