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

时间:2017-08-18 09:50:05

标签: rust

以下代码可以正常使用:

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 :(得分:4)

特征界限的生命周期有点特殊,Fn特征族具有特殊的生命周期省略规则。我们将深入研究,但首先,它是正确的明确注释版本:

fn get<F: for<'inp> Fn(&'inp [u8]) -> u8>(f: F) -> u8 {
    f(&[1, 2, 3])
}
哦,天啊,这for<'inp>做什么?它是一个所谓的更高排名的特质约束(HRTB),它用于使'inpf方面得到普遍的量化。为了充分理解这一点,我们需要理解一点理论。

谁有选择权?

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

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

此处,bar()是生命周期'a的通用名称。上面的语法为:“选择任何'a,并且bar()可以使用'a”。这意味着我们可以选择我们想要的任何'abar()也可以!我们是谁”? “我们”是来电者 - 一个叫bar的人。这在以后很重要:调用者选择通用参数。我们可以使用bar()调用&'static u8以及使用时间不长的引用。

现在你可能会问:是否存在调用者没有选择泛型参数的情况,但其他人呢?是的,有!可悲的是,它有点难以理解,因为它在今天的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?确切地说:来电者 - 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>”。而不是:“需要存在'a实现B”的生命周期参数Bar<'a>。关键是谁选择了哪个参数。

让我们使用真实姓名:

  • 如果来电者可以选择通用参数普遍量化
  • 如果被调用者可以选择,则存在量化

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*特征,那些生命周期会自动普遍量化,因为这通常是你想要的更高阶函数。

答案 1 :(得分:0)

感染代码似乎是:

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

Explanation