生命周期

让我们尝试通过为 &TicketStore 添加 IntoIterator 的实现来完成之前的练习,以便在 for 循环中获得最大的便利性。

首先,我们填写实现中最“明显”的部分:

#![allow(unused)]
fn main() {
impl IntoIterator for &TicketStore {
    type Item = &Ticket;
    type IntoIter = // 这里应该填什么类型?

    fn into_iter(self) -> Self::IntoIter {
        self.tickets.iter()
    }
}
}

type IntoIter 应该设置为什么类型呢?直观地,它应该是由 self.tickets.iter() 返回的类型,即 Vec::iter() 返回的类型。如果你查阅标准库文档,你会发现 Vec::iter() 返回了一个 std::slice::IterIter 的定义是:

#![allow(unused)]
fn main() {
pub struct Iter<'a, T> { /* 字段省略 */ }
}

'a 是一个 生命周期参数

生命周期参数

生命周期是 Rust 编译器用来追踪引用(无论是可变还是不可变)有效时间的 标签
引用的生命周期受到它所指向值的作用域限制。Rust 总是在编译时确保引用不会在其指向的值被丢弃后使用,以避免悬挂指针和使用已释放内存的错误。

这听起来应该很熟悉:在讨论所有权和借用时,我们已经看到过这些概念的应用。生命周期只是给特定引用的有效时间 命名 的方式。

当存在多个引用并且需要澄清它们彼此之间的 关联关系 时,命名变得重要。让我们看看 Vec::iter() 的签名:

#![allow(unused)]
fn main() {
impl <T> Vec<T> {
    // 稍作简化
    pub fn iter<'a>(&'a self) -> Iter<'a, T> {
        // [...]
    }
}
}

Vec::iter() 对一个名为 'a 的生命周期参数是泛型的。
'a 用来 绑定 Vec 的生命周期和由 iter() 返回的 Iter 的生命周期。通俗地说:由 iter() 返回的 Iter 不能超过创建它的 Vec 引用(&self)的生命周期。

这一点很重要,因为如前所述,Vec::iter 返回的是 Vec 元素的引用。如果 Vec 被丢弃,迭代器返回的引用将无效。Rust 必须确保这种情况不会发生,而生命周期就是它用来实施这一规则的工具。

生命周期省略

Rust 有一套称为 生命周期省略规则 的规则,在很多情况下允许你省略显式的生命周期注解。例如,Vec::iterstd 源代码中的定义是这样的:

#![allow(unused)]
fn main() {
impl <T> Vec<T> {
    pub fn iter(&self) -> Iter<'_, T> {
        // [...]
    }
}
}

Vec::iter() 的签名中没有显式的生命周期参数。省略规则意味着 iter() 返回的 Iter 的生命周期与 &self 引用的生命周期相关联。你可以把 '_ 当作 占位符 来理解,代表 &self 引用的生命周期。

有关生命周期省略的官方文档链接,请参阅 参考资料 部分。在大多数情况下,你可以依赖编译器告诉你何时需要添加显式的生命周期注解。

参考资料