以下代码工作正常:
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 回答
特质界限的生命期有点特殊,特征系列具有特殊的终身省略规则 . 我们将深入研究,但首先,它是正确的显式注释版本:
哦,天哪,这是什么意思?这是一个所谓的更高排名的特质界限(HRTB),它在这里被用来使
'inp
在f
方面得到普遍的量化 . 为了充分理解这一点,我们需要理解一点理论 .谁有选择?
我们来看一个简单的例子:
这里,
bar()
是生命周期的通用'a
. 上面的语法如下:“选择任何'a
,并且bar()
将与'a
一起使用” . 这意味着我们可以选择我们想要的任何'a
,并且bar()
有效!谁是"we"? "We"是调用者 - 调用bar
的调用者 . 这在以后很重要:调用者选择通用参数 . 我们可以使用&'static u8
调用bar()
以及使用不长的引用 .现在您可能会问:是否存在调用者没有更难理解的情况,因为它没有't occur too often in today'的Rust代码 . 但是我们试试:
这类似于上面的
bar()
函数,但现在life参数是在trait而不是函数上定义的 . 让我们使用这个特性:这不编译,打印与上面相同的错误 . 为什么?方法
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<>
语法来做到这一点:这很有效 . 我们在这里说的是:“对于任何生命周期参数
'a
,B
必须实现特征Bar<'a>
". Instead of: ",需要存在一个生命周期参数'a
,其中B
实现Bar<'a>
” . 这都是关于谁选择哪个参数 .让我们使用真实的名字:
如果调用者可以选择它,则通用参数是 universally quantified
如果被调用者可以选择它,则通用参数是 existentially quantified
Rust有什么作用?
回到你的例子:
这里我们遇到与上面相同的问题:
f
的生命周期参数是存在量化的 .f
的调用者无法选择生命周期参数 . 我们可以使用for<>
语法修复它,如上所示 .当你省略生命期时:
Rust编译器将为
Fn
特性系列做一些特殊的事情 . 你的F: Fn(&[u8])
去了F: for<'a> Fn<(&'a [u8],)>
. 如果你使用带有生命周期参数的Fn*
traits,那些生命周期会自动被普遍量化,因为这通常是你想要的更高阶函数 .感染代码似乎是:
Explanation