以下代码可以正常使用:
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 | | }
| |_^
答案 0 :(得分:4)
特征界限的生命周期有点特殊,Fn
特征族具有特殊的生命周期省略规则。我们将深入研究,但首先,它是正确的明确注释版本:
fn get<F: for<'inp> Fn(&'inp [u8]) -> u8>(f: F) -> u8 {
f(&[1, 2, 3])
}
哦,天啊,这for<'inp>
做什么?它是一个所谓的更高排名的特质约束(HRTB),它用于使'inp
在f
方面得到普遍的量化。为了充分理解这一点,我们需要理解一点理论。
让我们看一个简单的例子:
fn bar<'a>(x: &'a u8) {}
此处,bar()
是生命周期'a
的通用名称。上面的语法为:“选择任何'a
,并且bar()
可以使用'a
”。这意味着我们可以选择我们想要的任何'a
,bar()
也可以!我们是谁”? “我们”是来电者 - 一个叫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);
}
这很有效。我们在这里说的是:“对于任何生命周期参数'a
,B
必须实现特征Bar<'a>
”。而不是:“需要存在'a
实现B
”的生命周期参数Bar<'a>
。关键是谁选择了哪个参数。
让我们使用真实姓名:
回到你的例子:
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)