为什么在闭包中调用函数与在Rust中直接调用函数时,生命周期会有所不同?

Dav*_*lis 11 rust

在以下代码示例中:

fn default_values() -> &'static [u32] {
    static VALUES: [u32; 3] = [1, 2, 3];
    &VALUES
}

fn main() {
    let values: [u32; 3] = [4, 5, 6];
    let optional_values: Option<&[u32]> = Some(&values);

    // this compiles and runs fine 
    let _v = optional_values.unwrap_or_else(|| default_values());

    // this fails to compile
    let _v = optional_values.unwrap_or_else(default_values);
}
Run Code Online (Sandbox Code Playgroud)

最后一个语句无法编译:

error[E0597]: `values` does not live long enough
  --> src/main.rs:8:49
   |
8  |     let optional_values: Option<&[u32]> = Some(&values);
   |                                                 ^^^^^^ borrowed value does not live long enough
...
12 | }
   | - borrowed value only lives until here
   |
   = note: borrowed value must be valid for the static lifetime...
Run Code Online (Sandbox Code Playgroud)

我在想:

  1. 发生了什么导致最后两个语句之间的行为差​​异
  2. 是否第一种unwrap_or_else(|| default_values())是正确的处理方式,或者是否有更好的模式

tre*_*tcl 5

这是因为default_values实现Fn() -> &'static [u32],但不是for<'a> Fn() -> &'a [u32].特征是不变的,所以你不能强迫"实现的东西Fn() -> &'static [u32]"到"实现的东西Fn() -> &'a [u32]"(对于一些'a小的东西'static),尽管从逻辑上讲,default_values它可以满足两者.

当它在闭包中调用时,default_values()返回a &'static [u32],但它可以立即强制转换为a &'a u32,使得闭包本身能够实现Fn() -> &'a [u32](&'a由编译器确定).

至于为什么添加as fn() -> &'static [u32]工作,我假设编译器可以识别函数指针类型fn() -> &'static [u32]能够实现Fn() -> &'a [u32]任何'a.我不确定为什么它也不能用于普通的功能和闭包; 或许未来的编译器版本可能足够智能以允许原始代码.

另一个解决方案是制作default_values一个可以实现所需Fn特性的类型:

fn default_values<'a>() -> &'a [u32] {
    static VALUES: [u32; 3] = [1, 2, 3];
    &VALUES
}
Run Code Online (Sandbox Code Playgroud)

'static这里的签名不是说"这是一个返回引用的函数",而是说"这是一个可以返回任何生命周期的引用的函数".我们知道"任何生命周期的参考"必须是一个'static参考,但编译器认为签名是不同的,因为这个签名具有额外的自由度.此更改足以使您的原始示例编译.