我相信这个函数声明告诉Rust函数输出的生命周期与它的s
参数的生命周期相同:
fn substr<'a>(s: &'a str, until: u32) -> &'a str;
^^^^
在我看来,编译器只需要知道这个(1):
fn substr(s: &'a str, until: u32) -> &'a str;
函数名后面的注释<'a>
是什么意思?为什么编译器需要它,它用它做什么?
(1):我知道由于终身缺勤,它需要知道的更少。但这个问题是关于明确指定生命周期。
答案 0 :(得分:29)
让我扩展先前的答案......
注释是什么&lt;'a&gt;函数名后面是什么意思?
我不会使用“注释”这个词。与<T>
引入通用类型参数非常相似,<'a>
引入了通用生存期参数。如果不首先引入它们,就不能使用任何通用参数,对于通用函数,这个引入恰好在它们的名称之后发生。您可以将通用函数视为一系列函数。因此,基本上,您可以为每个通用参数组合获得一个函数。 substr::<'x>
将成为该函数系列的特定成员,有效期为'x
。
如果您不清楚我们何时以及为何要明确生命,请继续阅读...
生命周期参数始终与所有引用类型相关联。当你写
fn main() {
let x = 28374;
let r = &x;
}
编译器知道x存在于用大括号括起来的main函数的作用域中。在内部,它使用一些生命周期参数来标识此范围。对我们来说,它没有命名。当您获取x
的地址时,您将获得特定参考类型的值。引用类型是二维引用类型族的一种。一个轴是参考指向的类型,另一个轴是用于两个约束的生命周期:
这些限制在Rust的记忆安全故事中起着至关重要的作用。这里的目标是避免悬挂引用。我们想排除指向某些内存区域的引用,我们不再允许它们使用它,因为它曾经指向的东西不再存在。
混淆的一个潜在原因可能是寿命参数在大多数时间是不可见的。但这并不意味着他们不存在。引用始终在其类型中具有生命周期参数。但是这样的生命周期参数不必具有名称,并且大多数时候我们不需要提及它,因为编译器可以自动为生命周期参数分配名称。这被称为“终身省略”。例如,在以下情况中,您不会看到所提到的任何生命周期参数:
fn substr(s: &str, until: u32) -> &str {…}
但是可以这样写。它实际上是更明确的
的快捷语法fn substr<'a>(s: &'a str, until: u32) -> &'a str {…}
这里,编译器自动为“输入生命周期”和“输出生命周期”分配相同的名称,因为它是一种非常常见的模式,很可能正是您想要的。因为这种模式是如此常见,编译器让我们逃避而不说任何关于生命期的事情。它假定这个更明确的形式是基于一些“终身省略”规则(至少记录在案here)的意思
在某些情况下,显式生命周期参数不可选。例如,如果你写
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 { … }
因此,对于每个引用,引入单独的生命周期参数。但是,此签名中没有关于生命周期参数如何相互关联的信息。此通用函数的用户可以使用任何生命周期。而这是身体内部的一个问题。我们正在尝试返回x
或y
。但x
的类型为&'a T
。这与返回类型&'c T
不兼容。 y
也是如此。由于编译器对这些生命周期如何相互关联一无所知,因此将这些引用作为&'c T
类型的引用返回是不安全的。
从&'a T
类型的值转到&'c T
是否安全?是。如果生命周期'a
等于或更高,则生命周期'c
是安全的。或者换句话说'a: 'c
。所以,我们可以写这个
fn min<'a, 'b, 'c, T: Ord>(x: &'a T, y: &'b T) -> &'c T
where 'a: 'c, 'b: 'c
{ … }
并在没有编译器抱怨函数体的情况下逃脱它。但它实际上不必要复杂。我们也可以简单地写一下
fn min<'a, T: Ord>(x: &'a T, y: &'a T) -> &'a T { … }
并为所有内容使用单个生命周期参数。编译器能够将'a
推导为调用站点上参数引用的最小生命周期,因为我们对两个参数使用了相同的生命周期名称。而这一生命正是我们对返回类型所需要的。
我希望这能回答你的问题。 :) 干杯!
答案 1 :(得分:16)
注释是什么&lt;&#39; a&gt;函数名后面是什么意思?
fn substr<'a>(s: &'a str, until: u32) -> &'a str;
^^^^
这是一个通用的生命周期参数。它类似于通用类型参数(通常被视为<T>
),因为函数的调用者可以决定生命周期是什么。就像你说的那样,结果的生命周期与第一个参数的生命周期相同。
所有生命周期名称都是等效的,除了一个:'static
。这个生命周期预先设定为&#34;保证在整个计划生命期内生活#34;。
最常见的生命周期参数名称可能是'a
,但您可以使用任何字母或字符串。单个字母最常见,但任何snake_case
标识符都可以接受。
除非具有非常好的人体工程学优势,否则Rust通常倾向于明确表达。终生,elision可以照顾85%以上的病例,这似乎是一个明显的胜利。为什么编译器需要它,它用它做什么?
类型参数与其他类型存在于同一名称空间中 - T
是泛型类型还是有人为结构命名?因此,类型参数需要具有显式注释,该注释显示T
是参数而不是实际类型。但是,生命周期参数不会出现同样的问题,因此不是原因。
相反,明确列出类型参数的主要好处是因为您可以控制多个参数的交互方式。一个无意义的例子:
fn better_str<'a, 'b, 'c>(a: &'a str, b: &'b str) -> &'c str
where 'a: 'c,
'b: 'c,
{
if a.len() < b.len() { a } else { b }
}
我们有两个字符串,并说输入字符串可能具有不同的生命周期,但必须比结果值的生命周期更长。
另一个例子,pointed out by DK,结构可以有自己的生命周期。我让这个例子也有点废话,但希望能传达这一点:
struct Player<'a> {
name: &'a str,
}
fn name<'p, 'n>(player: &'p Player<'n>) -> &'n str {
player.name
}
生命周期可能是Rust中令人费解的部分之一,但是当你开始掌握它们时它们非常棒。
答案 2 :(得分:1)
fn subslice<T>(s: &[T], until: u32) -> &[T] {
&s[..until as usize]
}
fn substr(s: &str, until: u32) -> &str {
&s[..until as usize]
}
注释只声明函数中使用的生命周期,与通用参数int w=1;
do {
Scanner keyboard = new Scanner(System.in);
System.out.println(w);
if(w%2==0)
System.out.println("Even value : "+ w);
w++;
}
while (w<=2015);
}
}
完全相同。
{{1}}
请注意,在您的示例中,可以推断出所有生命周期。
{{1}}