首页 > 解决方案 > 为什么在闭包中调用函数与在 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);
}

最后一条语句无法编译:

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...

我在想:

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

标签: rust

解决方案


发生这种情况是因为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
}

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


推荐阅读