第一个显式生命周期指定符在impl上是什么意思?

35

impl 上有三个不同的生命周期限定符:

impl<'a> Type<'a> {
    fn my_function(&self) -> &'a u32 {
        self.x
    }
}

Type<'a> 表示在这个impl声明中有一个生命周期。返回类型 -> &'a u32 上的生命周期表示接收返回值的变量在某个时间点之前不能被释放...在什么之前?在 Type 类型对象消失之前吗?

与此有何区别:

impl TextEditor {
    //Other methods omitted ...

    pub fn get_text<'a>(&'a self) -> &'a String {
        return &self.text;
    }
}

这里指定返回类型不要在 &'a self 生命周期结束前销毁。

最后一个声明是否只为该方法声明了一个生命周期,而另一个则为 impl 声明中的每个方法(和相关函数)声明了生命周期?


2
Type<'a> 可能包含依赖于 'a 的内容。例如,在 slice :: Iter <'a,T> 中,'a 是相应切片的生命周期。 [as_slice(&self) - >&'a [T]] (https://doc.rust-lang.org/std/slice/struct.Iter.html#method.as_slice)可以检索它。 - mcarton
3个回答

32

'a是两种情况下的生命周期参数。这是一种通用的参数,因此Type的每个使用或get_text的每个使用都可以为该通用参数选择不同的“值”。实际寿命从不由程序员明确选择,除非您使用'static

编译器将推断出每个Type值或每个get_text使用的'a应该是什么。

impl<'a>引入了整个impl块的新生命周期参数。然后在类型中使用:impl<'a> Type<'a> { .. }

'a的确切含义取决于它在Type定义中的用法。从您的示例中可以看出,Type是这样的:

struct Type<'a> {
    x: &'a u32,
}

这个定义的意思是:对于每个生命周期'a,定义Type包含一个引用x: &'a u32。因此,Type是泛型的,可以存储任何生命周期的引用。

impl<'a> Type<'a> { .. }的意思是:对于每个生命周期'a,为类型Type<'a>定义方法。

既然我们现在知道了Type的结构定义,我们就知道impl块内的'a参数始终等于Type的x字段中的引用的生命周期。

返回类型中的&'a u32表示接收返回值的变量在...之前不应该死亡。在什么之前?在Type对象之前吗?

'a是存储在Type<'a>值中的引用的生命周期,并且它与Type值本身没有其他关系。唯一的规则是'a必须比Type值本身更长,因为不允许在其生命周期结束后存储引用。因此,实际上,我们可以保留那个&'a u32,直到Type值死亡的时刻,甚至更久。


impl TextEditor {
    //Other methods omitted ...

    pub fn get_text<'a>(&'a self) -> &'a String {
        return &self.text;
    }
}

这是很常见的。 &self 是指向 self 值(一种借用)的引用,方法 get_text 再次是一个通用类型。它有一个通用参数 - 生命周期参数。

它表示,对于任何生命周期'a,将self作为引用&'a self(该生命周期的引用)进行借用,并返回一个与相同生命周期的字符串的引用。

在输入&self和输出&String都使用了相同的参数,这意味着它们是连接在一起的,只要返回的字符串引用存在,Rust就会将self视为被借用的状态。

同样,方法get_text是通用的,编译器将为每个方法的使用选择一个“值”'a。它是一种可以返回字符串的不同长时间借用的方法,具体取决于您允许其借用self的时间有多长。有时它会选择一个长的生命周期,以便您可以长时间保留返回值&String。如果您根本不使用返回值,那么某些get_text的用法将使用较短的生命周期。

在这种情况下,由于我们看到&'a String直接绑定在TextEditor值的借用&'a self上,我们知道我们最多只能保留字符串引用和TextEditor值的借用持续时间相同。


将<'a>符号视为泛型对我来说是一个“恍然大悟”的时刻。不知道为什么以前没有想到过。 - muodov

28

重新表述 Rust 代码:

impl<'a>

如果你给我一生的时间......(编译器通常根据上下文情况自动填写该类型)

         Type<'a> {

“...我将描述如何实现Type<'a>”。所以Type可能包含需要生命周期引用的引用。

    fn my_function(&self) -> &'a u32 {

"...并且给出 Type<'a> 的引用,你可以调用 my_function() 来获取一个生命周期为 'au32 引用。" 请注意,&self 引用的生命周期与 'a 不直接相关;它可以更短(但通常不会比 'a 更长,因为类型不能超出包含的引用的生命周期)。

在第二种情况下:

impl TextEditor {

以下是如何实现一个非生命周期参数化类型 TextEditor 的方法...

pub fn get_text<'a>

假设有一个寿命参数 'a,你可以选择它(这是一个输入参数)...

                   (&'a self)

“......并引用存在至少为'aTextEditor

                             -> &'a String {

"......你可以调用get_text方法并获得指向同一生存期的String的借用引用."

更实际地说,这实际上意味着String是直接从TextEditor重新借用的 - 只要该String引用仍然存在,&self借用就被认为仍然处于活动状态,您将无法获取任何&mut引用。


6
谢谢,这非常有帮助!我发现我可以理解Rust的大多数概念,但在实践中将它们组合起来时常常感到困难。将简洁的语法转化为说明性的散文是理解正在进行的事情的好方法。我希望有更多随代码阅读的例子 :) - dlukes

1
问题的本质与`'a`的作用域有关:
情况 'a的作用域
impl<'a> { fn } 'a可以用于内部的任何函数
impl { fn<'a> } 'a对每个函数都是特定的
在这两种情况下,`'a`是一个通用的生命周期参数。对于任何函数来说,`'a`的语义是没有区别的
这个表格说明了作用域的差异,但它并没有突出在`impl`级别指定生命周期的实际好处。在需要多个方法与相同生命周期约束一起工作的情况下,将生命周期语法提升到`impl`级别可以在该块中的所有方法之间建立一致的约束。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接