让我进一步阐述前面的答案…
What does the annotation <'a> after the function name mean?
我不会用"注释"这个词.就像<T>
引入通用type参数一样,<'a>
引入通用lifetime参数.如果不先介绍泛型参数,就不能使用任何泛型参数.对于泛型函数,这种介绍就发生在它们的名称之后.你可以把泛型函数看作是一系列函数.所以,本质上,每一个泛型参数的组合都有一个函数.substr::<'x>
将在一生中成为该功能家族的特定成员'x
.
如果你不清楚什么时候以及为什么我们必须明确地描述生命,请继续阅读…
生存期参数始终与所有引用类型关联.当你写作的时候
fn main() {
let x = 28374;
let r = &x;
}
编译器知道x位于主函数的作用域中,并用大括号括起来.在内部,它用一些生存期参数标识这个范围.对我们来说,它还没有命名.当你取地址x
时,你会得到一个特定引用类型的值.引用类型是二维引用类型族的一种成员.一个轴是参考点的类型,另一个轴是用于两个约束的生命周期:
- 引用类型的lifetime参数表示可以保留该引用多长时间的上限
- 引用类型的lifetime参数表示可以使引用指向的对象的生命周期的下限.
总之,这些限制在Rust的记忆安全故事中起着至关重要的作用.这里的目标是避免悬而未决的引用.我们想排除指向某个我们不再被允许使用的内存区域的引用,因为它用来指向的东西已经不存在了.
造成混淆的一个潜在原因可能是,生命周期 参数在大多数情况下是不可见的.但这并不意味着他们不在那里.引用always的类型中有一个生存期参数.但是这样一个生命周期参数不一定要有名字,而且大多数时候我们也不需要提及它,因为编译器可以自动为生命周期参数指定名字.这被称为"终身省略".例如,在以下情况下,您不需要提及任何生命周期 参数:
fn substr(s: &str, until: u32) -> &str {…}
但这样写也没关系.这实际上是一种更明确的语法
fn substr<'a>(s: &'a str, until: u32) -> &'a str {…}
在这里,编译器会自动为"输入生存期"和"输出生存期"指定相同的名称,因为这是一种非常常见的模式,很可能正是您想要的.因为这种模式非常常见,编译器让我们不用说任何关于生命周期的事情就可以逃脱.根据两条"终身省略"规则(至少有here条记录),这种更明确的形式就是我们的意思
在某些情况下,explicit个生命周期 参数是not个可选参数.例如,如果你写
fn min<T: Ord>(x: &T, y: &T) -> &T {
if x <= y {
x
} else {
y
}
}
编译器会抱怨,因为它会将上述声明解释为
fn min<'a, 'b, 'c, T: Ord>(x: &'a T, y: &'b T) -> &'c T { … }
因此,对于每个参考,都会引入一个单独的生命周期 参数.但是,在这个签名中没有关于生命周期参数如何相互关联的信息.这个通用函数的用户可以使用any个生命周期.这是它体内的一个问题.我们想退回x
或y
.但x
型是&'a T
型.这与返回类型&'c T
不兼容.y
也是如此.由于编译器不知道这些生命周期如何相互关联,因此将这些引用作为&'c T
类型的引用返回是不安全的.
从&'a T
型值到&'c T
型值安全吗?对如果'a
的生命周期 等于or greater,那么'c
的生命周期 是安全的.或者换句话说'a: 'c
.所以,我们写这个
fn min<'a, 'b, 'c, T: Ord>(x: &'a T, y: &'b T) -> &'c T
where 'a: 'c, 'b: 'c
{ … }
而且不用让编译器抱怨函数的主体就可以顺利完成.但它实际上是unnecessarily复杂.我们也可以简单地写
fn min<'a, T: Ord>(x: &'a T, y: &'a T) -> &'a T { … }
并对所有内容使用一个生命周期参数.编译器能够推断'a
是调用站点参数引用的最短生存期,因为我们对两个参数使用了相同的生存期名称.这个生命周期正是我们需要的返回类型.
我希望这能回答你的问题.:)