首页 文章

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

提问于
浏览
6

我想这个问题一般都是关于生命周期的,但我特别是因为你不能写出他们的类型而对闭包有困难 .

这个例子有点做作 - 我刚刚开始学习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 取得闭包的所有权),则此代码将起作用,但由于闭包的大小不合适,我必须将其作为参考传递 .

是对的吗?如何使此代码有效?

2 回答

  • 8

    您要问的有两件事:

    • 为没有可指定类型名称的内容指定typename

    • 让闭包的寿命比定义它的块长 .

    第一个问题是通过NOT指定typename来解决的,并让rust的类型推断完成工作 .

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

    第二个问题是通过不试图让关闭更长寿,但通过使其“按 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));
        }
    }
    
  • 6

    借用不拥有他们指向的东西 . 你的问题是你正在借用一个暂时存在的临时存储,它会在它被存储到任何地方后立即停止存在 . 如果它有帮助,考虑借用不借用值,他们借用存储,而临时只有暂时存储 .

    如果您希望在任何给定时间内借入某些东西,您必须从至少持续这么长时间的存储中借用 . 在这种情况下,因为您希望将借用存储在 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):如果你需要返回 list 或者以某种方式将其持续到单个函数调用之外,请注意没有办法扩展借用 . 在这种情况下,您需要做的是将 list 更改为拥有的盒装闭包向量(即 Vec<Box<Fn() -> i32>> ) .

相关问题