首页 文章

代码使用elided生命周期,而不是明确的

提问于
浏览
3

以下代码工作正常:

fn get<F: Fn(&[u8]) -> u8>(f: F) -> u8 {
    f(&[1, 2, 3])
}

但是,当我向其添加显式生命周期信息时,它不会:

fn get<'inp, F: Fn(&'inp [u8]) -> u8>(f: F) -> u8 {
    f(&[1, 2, 3])
}

编译器在工作代码中推断出的生命周期是多少?

我正在使用Rust 1.18.0 .

错误消息是:

error: borrowed value does not live long enough
 --> test.rs:4:8
  |
4 |     f(&[1, 2, 3])
  |        ^^^^^^^^^ does not live long enough
5 | }
  | - temporary value only lives until here
  |
note: borrowed value must be valid for the lifetime 'inp as defined on the body at 3:49...
 --> test.rs:3:50
  |
3 |   fn get<'inp, F: Fn(&'inp [u8]) -> u8>(f: F) -> u8 {
  |  __________________________________________________^
4 | |     f(&[1, 2, 3])
5 | | }
  | |_^

2 回答

  • 0

    特质界限的生命期有点特殊,特征系列具有特殊的终身省略规则 . 我们将深入研究,但首先,它是正确的显式注释版本:

    fn get<F: for<'inp> Fn(&'inp [u8]) -> u8>(f: F) -> u8 {
        f(&[1, 2, 3])
    }
    

    哦,天哪,这是什么意思?这是一个所谓的更高排名的特质界限(HRTB),它在这里被用来使 'inpf 方面得到普遍的量化 . 为了充分理解这一点,我们需要理解一点理论 .


    谁有选择?

    我们来看一个简单的例子:

    fn bar<'a>(x: &'a u8) {}
    

    这里, bar() 是生命周期的通用 'a . 上面的语法如下:“选择任何 'a ,并且 bar() 将与 'a 一起使用” . 这意味着我们可以选择我们想要的任何 'a ,并且 bar() 有效!谁是"we"? "We"是调用者 - 调用 bar 的调用者 . 这在以后很重要:调用者选择通用参数 . 我们可以使用 &'static u8 调用 bar() 以及使用不长的引用 .

    现在您可能会问:是否存在调用者没有更难理解的情况,因为它没有't occur too often in today'的Rust代码 . 但是我们试试:

    trait Bar<'a> {
        fn bar(&self, x: &'a u8);
    }
    

    这类似于上面的 bar() 函数,但现在life参数是在trait而不是函数上定义的 . 让我们使用这个特性:

    fn use_bar<'a, B: Bar<'a>>(b: B) {
        let local = 0u8;
        b.bar(&local);
    }
    

    这不编译,打印与上面相同的错误 . 为什么?方法 b.bar() 需要生命周期 'a 的引用 . 但谁在这里选择 'a ?确切地说:调用者 - 调用者 of use_bar() ,而不是 bar() 的调用者!所以 use_bar() 的调用者可以选择 'static 生命周期;在这种情况下,很容易看出我们的 &local 不符合终身要求 .

    请注意 use_bar() 的调用者选择 'a 以及 B . 实例化 use_bar() 后, B 是固定类型, B::bar() 仅适用于一个特定的生命周期 . 这意味着 bar() 的调用者无法选择生命周期,但 bar() 本身就选择了它!

    我们想要什么呢?我们希望 use_bar() 选择 bar() 调用的生命周期 . 我们可以使用 for<> 语法来做到这一点:

    fn use_bar<B: for<'a> Bar<'a>>(b: B) {
        let local = 0u8;
        b.bar(&local);
    }
    

    这很有效 . 我们在这里说的是:“对于任何生命周期参数 'aB 必须实现特征 Bar<'a> ". Instead of: ",需要存在一个生命周期参数 'a ,其中 B 实现 Bar<'a> ” . 这都是关于谁选择哪个参数 .

    让我们使用真实的名字:

    • 如果调用者可以选择它,则通用参数是 universally quantified

    • 如果被调用者可以选择它,则通用参数是 existentially quantified

    Rust有什么作用?

    回到你的例子:

    fn get<'inp, F: Fn(&'inp [u8]) -> u8>(f: F) -> u8 {
        f(&[1, 2, 3])
    }
    

    这里我们遇到与上面相同的问题: f 的生命周期参数是存在量化的 . f 的调用者无法选择生命周期参数 . 我们可以使用 for<> 语法修复它,如上所示 .

    当你省略生命期时:

    fn get<F: Fn(&[u8]) -> u8>(f: F) -> u8 {
        f(&[1, 2, 3])
    }
    

    Rust编译器将为 Fn 特性系列做一些特殊的事情 . 你的 F: Fn(&[u8]) 去了 F: for<'a> Fn<(&'a [u8],)> . 如果你使用带有生命周期参数的 Fn* traits,那些生命周期会自动被普遍量化,因为这通常是你想要的更高阶函数 .

  • 4

    感染代码似乎是:

    fn get<F : for<'inp> Fn(&'inp[u8]) -> u8>(f: F) -> u8 {
        f(&[1,2,3])
    }
    

    Explanation

相关问题