首页 文章

错误的真正含义是“闭包可能比现在的功能更长”?

提问于
浏览
0

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

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,
}

我得到以下编译错误:

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,
  |              ^^^^^^^^^^^^^^^^^^^^^^^^

我在struct中添加了一个生命周期参数,它编译并运行良好:

struct ClosureTest<'a, T>
where
    T: FnMut(&str) -> &'a str,
{
    cls: T,
}

两个变量( captured_valclosure_parameter )都在相同的范围内,但似乎编译器没有看到它们具有相同的生命周期而没有生命周期参数 'a 我添加到 ClosureTest<T> . 这是因为Rust的封闭期终生推理?

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

error[E0373]: closure may outlive the current function, but it borrows `captured_val`, which is owned by the current function

封闭怎么能比现在的功能更长久?闭包是在当前函数中定义的,所以我认为闭包将在当前函数的末尾消失 .

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

1 回答

  • 1

    The short version: 添加显式生命周期注释使 guarantee 关于 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,
    }
    

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

    通过修改 ClosureTest 以包含生命周期,您将说明以下内容:

    • 一个 ClosureTest 对象 obj 有一些生命周期 'a

    • obj.cls 返回的 str 引用在 obj 上定义为 'a 的整个生命周期内生存,如果不再

    • 因此, obj.cls 返回的任何引用都与返回它的 ClosureTest 具有相同的范围 . obj 将与其闭包引用的对象同时删除,或者更快 . 换句话说,任何 ClosureType<'a, T> where T: FnMut(&str) -> &'a str 只能在对象 T 的返回引用存在时生存,并且必须在删除该对象时删除 .

    借用检查器不会对原始值感到恼火,因为 ctcaptured_value 具有不同的生命周期,这是因为它们可能具有不同的生命周期 - 因此'may'中'可能比借来的值更长 captured_val ' . 当你在你的例子中添加了lifetime参数时,借用检查器现在可以确认只有 captured_valct 一样存在它才能进行编译(它会在 main 结束时被删除) .

相关问题