错误“闭包可能比当前函数寿命更长”的真正含义是什么?

Par*_*olu 4 closures lifetime rust

我写了以下内容来测试闭包:

fn main() {
    let captured_val = "a captured value".to_string();
    let closure_parameter = "a parameter value".to_string();

    let mut ct = ClosureTest {
        cls: |closure_parameter| &captured_val,
    };
    println!("{}", (ct.cls)(&closure_parameter));
}

struct ClosureTest<T>
where
    T: FnMut(&str) -> &str,
{
    cls: T,
}
Run Code Online (Sandbox Code Playgroud)

我收到以下编译错误:

fn main() {
    let captured_val = "a captured value".to_string();
    let closure_parameter = "a parameter value".to_string();

    let mut ct = ClosureTest {
        cls: |closure_parameter| &captured_val,
    };
    println!("{}", (ct.cls)(&closure_parameter));
}

struct ClosureTest<T>
where
    T: FnMut(&str) -> &str,
{
    cls: T,
}
Run Code Online (Sandbox Code Playgroud)

我向该结构添加了一个生命周期参数,它编译并运行良好:

struct ClosureTest<'a, T>
where
    T: FnMut(&str) -> &'a str,
{
    cls: T,
}
Run Code Online (Sandbox Code Playgroud)

两个变量 (captured_val和) 都在同一范围内,但如果没有我添加到 的closure_parameter生命周期参数,编译器似乎看不到它们具有相同的生命周期。这是因为 Rust 对闭包的生命周期推断吗?'aClosureTest<T>

我不明白收到的错误消息中的第一行:

error[E0373]: closure may outlive the current function, but it borrows `captured_val`, which is owned by the current function
 --> src/main.rs:6:14
  |
6 |         cls: |closure_parameter| &captured_val,
  |              ^^^^^^^^^^^^^^^^^^^  ------------ `captured_val` is borrowed here
  |              |
  |              may outlive borrowed value `captured_val`
help: to force the closure to take ownership of `captured_val` (and any other referenced variables), use the `move` keyword
  |
6 |         cls: move |closure_parameter| &captured_val,
  |              ^^^^^^^^^^^^^^^^^^^^^^^^
Run Code Online (Sandbox Code Playgroud)

闭包怎么可能比当前函数更长寿?闭包仅在当前函数中定义,因此我认为闭包将在当前函数末尾消失。

错误消息的真正含义是什么?为什么像我一样通过添加生命周期参数来解决它?

Mut*_*pus 5

简短版本:添加显式生命周期注释可以保证没有它就ClosureTest不一定隐含什么。


长版

假设你这样做了:

fn main() {
    let closure_parameter = "a parameter value".to_string();

    let returned_ct = do_something(v);

    println!("{}", (returned_ct.cls)(&closure_parameter));
}

fn do_something() -> ClosureTest {
    let captured_val = "a captured value".to_string();

    let mut ct = ClosureTest {
        cls: |param| &captured_val,
    };
    ct // !!!
}

struct ClosureTest<T>
where
    T: FnMut(&str) -> &str,
{
    cls: T,
}
Run Code Online (Sandbox Code Playgroud)

在标有 的行上!!!,请注意 已ct作为返回值移出函数。该对象现在“存在”于 中main,但它包含对结束时被删除的内容的引用do_something。ct.cls返回对 的引用,如果将其移出函数,captured_val该引用将不再存在。ct

通过修改ClosureTest以包含生命周期,您的意思是:

  • 对象ClosureTest有obj一定的生命周期'a
  • strlife返回的引用在定义为onobj.cls的整个生命周期内(如果不是更长的话)'aobj
  • 因此,返回的任何引用与返回它obj.cls的引用具有相同的范围ClosureTest。obj将与其闭包引用的对象同时删除,或更早删除。换句话说,anyClosureType<'a, T> where T: FnMut(&str) -> &'a str只能在对象T的返回引用存在时存在,并且必须在该对象被删除时删除。

借用检查器不会对原始值感到恼火,因为ct和captured_value 具有不同的生命周期,这是因为它们可能有不同的生命周期 - 因此“可能比借用的价值”中的“可能captured_val”。当您在示例中添加生命周期参数时,借用检查器现在可以确认仅当captured_val生存期与 一样长时才应该编译ct,它确实这样做了(它们都在 末尾被删除main)。