如何声明一个比其封闭块寿命更长的闭包

How to declare a closure that lives longer than its enclosing block

我想这个问题一般是关于生命周期的,但是我在闭包方面遇到了困难,特别是因为你不能写出它们的类型。

这个例子有点做作 - 我刚开始学习 Rust,这是我一直挂断的事情。

此程序无法编译:

fn main () {
    let mut list: Vec<&Fn() -> i32> = Vec::new();

    {
        list.push(&|| 1);
    }
}

因为:

src/main.rs:5:25: 5:24 error: borrowed value does not live long enough
src/main.rs:5         list.push(&|| 1);
                                 ^~~~
src/main.rs:2:50: 7:2 note: reference must be valid for the block suffix following statement 0 at 2:49...
src/main.rs:2     let mut list: Vec<&Fn() -> i32> = Vec::new();
src/main.rs:3
src/main.rs:4     {
src/main.rs:5         list.push(&move || 1);
src/main.rs:6     }
src/main.rs:7 }
src/main.rs:5:9: 5:26 note: ...but borrowed value is only valid for the statement at 5:8
src/main.rs:5         list.push(&|| 1);
                      ^~~~~~~~~~~~~~~~~
src/main.rs:5:9: 5:26 help: consider using a `let` binding to increase its lifetime
src/main.rs:5         list.push(&|| 1);
                      ^~~~~~~~~~~~~~~~~

我从这个错误中了解到闭包的生命周期限于 块内的语句,但它需要为 main 的整个主体生存。

我知道(或者,我认为)将闭包作为参考传递给 push 意味着 push 只是借用了闭包,并且所有权将返回给块。如果我可以将闭包提供给 push(即,如果 push 获得闭包的所有权),此代码将起作用,但由于闭包未调整大小,我必须将其作为参考传递。

是吗?我怎样才能使这段代码工作?

借用不拥有他们指向的东西。你的问题是你借用了一个 temporary ,它在借用后将不复存在,因为你没有将它存储在任何地方。如果有帮助,请考虑借用不借用值,它们借用存储,临时存储只有瞬时存储。

如果您想借用某物以在任何特定时期内持续使用,您必须从将持续至少的存储中借用长。在这种情况下,因为您想将借用存储在 Vec 中,这意味着您借用的任何存储都必须 Vec 还长 。因此:

fn main () {
    let closure;
    let mut list: Vec<&Fn() -> i32> = Vec::new();

    {
        closure = || 1;
        list.push(&closure);
    }
}

注意closure定义在之前list。在 Rust 中,值在其作用域的末尾以相反的词法顺序被删除,因此在 list 之后定义的任何变量都必须在它之前被删除,从而导致 list 包含无效指针。

如果要推送多个闭包,则每个闭包都需要一个单独的变量。

为了防止可能的 "my actual problem isn't this simple" 附录 (:P):如果您需要 return list 或以某种方式将其保留到单个函数调用之外,请注意 return list =30=]无法延长借用。在那种情况下,您需要做的是将 list 更改为 owned 的向量,盒装闭包( Vec<Box<Fn() -> i32>>).

你问的有两件事:

  1. 为没有可指定类型名称的东西指定类型名称
  2. 让闭包比定义它的块活得更长。

第一个问题已通过不指定类型名称并让 Rust 的类型推断完成工作来解决。

let mut list: Vec<_> = Vec::new();

第二个问题已解决,方法是不尝试延长闭包的寿命,而是将其设为 "by value" 以便您可以移动它。这强制你的闭包不引用任何东西,但拥有所有捕获的值。

for i in 0..10 {
    list.push(move || i);
}

现在这给我们带来了一个新问题。如果我们向 Vec 添加不同的闭包,类型将不匹配。因此,要实现这一点,我们需要将闭包装箱。

fn main () {
    let mut list: Vec<Box<Fn() -> i32>> = Vec::new();

    for i in 0..10 {
        list.push(Box::new(move|| i));
    }

    {
        list.push(Box::new(move|| 42));
    }
}