54、生命周期
Lifetimes(生命周期)
让我们尝试通过为&TicketStore
添加一个IntoIterator
的实现来完成前面的练习,以最大程度地方便使用for
循环。
让我们从填写实现中最“明显”的部分开始:
impl IntoIterator for &TicketStore {
type Item = &Ticket;
type IntoIter = // What goes here? 这里有什么?
fn into_iter(self) -> Self::IntoIter {
self.tickets.iter()
}
}
type IntoIter
应该设置为什么呢?
直观地说,应该是self.tickets.iter()
返回的类型,即Vec::iter()
返回的类型。
如果我们查看标准库文档,我们可以发现Vec::iter()
返回了一个std::slice::Iter
,Iter
的定义是:
pub struct Iter<'a, T> { /* fields omitted */ }
`a
是一个生命周期参数。
生命周期参数
生命周期是Rust编译器用来跟踪引用(可变或不可变)有效时间的标签。
reference
(名词,引用)的生命周期受其引用值的作用域限制。Rust始终确保在编译时,reference
不会在其所引用的值被丢弃后再被使用,以避免悬空指针和use-after-free
错误。
这听起来很熟悉:我们讨论所有权和借用的时候,我们已经看到了这些概念的实际应用。生命周期只不过是一种命名引用有效时间的方式而已。
当我们有多个引用时,并需要明确它们之间的关系时,命名就变得很重要。让我们先看看Vec::iter()
的签名:
impl <T> Vec<T> {
// Slightly simplified
pub fn iter<'a>(&'a self) -> Iter<'a, T> {
// [...]
}
}
我只找到了这个:
感觉好像差不太多……(很明显不是一个东西好吧)
好的,言归正传。
Vec::iter()
是一个泛型函数,其参数包含一个生命周期标记 'a
。'a
用于将Vec
的生命周期和iter()
返回的生命周期联系在一起。简单来说:iter()
返回的类型,不能比创建它的Vec
引用(&self
)更长。
这很重要,正如我们学到的,Vec::iter
返回一个对Vec
元素引用的迭代器。如果删除Vec
,迭代器返回的引用将无效。Rust必须确保这种情况不发生,而生命周期就是它用来执行此规则的工具。
生命周期省略
Rust有一组规则,称为生命周期省略规则,允许我们在很多情况下省略显式的生命周期注释。例如,Vec::iter()
的定义在std
的源代码中如下所示:
impl <T> Vec<T> {
pub fn iter(&self) -> Iter<'_, T> {
// [...]
}
}
Vec::iter()
的签名中不存在显式生命周期参数。省略规则意味着iter()
返回的Iter
的生命周期与&self
引用的生命周期相关联。我们可以将'_
视为&self
的引用生命周期的占位符。
这里给了有关生命周期省略的一些官方文档链接:
在大多数情况下,我们可以依靠编译器告诉我们何时需要添加显式生命周期注释(也就是说该添加的时候,编译器会告诉咱让咱写上去……)这么好的编译器,能不能给我C++。Java也整一个捏~( ̄▽ ̄)~*
练习
use ticket_fields::{TicketDescription, TicketTitle};
// TODO: Implement the `IntoIterator` trait for `&TicketStore` so that the test compiles and passes.
// 实现 '&TicketStore' 的 'IntoIterator' 特征,以便测试编译并通过。
#[derive(Clone)]
pub struct TicketStore {
tickets: Vec<Ticket>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Ticket {
pub title: TicketTitle,
pub description: TicketDescription,
pub status: Status,
}
#[derive(Clone, Debug, Copy, PartialEq)]
pub enum Status {
ToDo,
InProgress,
Done,
}
impl TicketStore {
pub fn new() -> Self {
Self {
tickets: Vec::new(),
}
}
pub fn add_ticket(&mut self, ticket: Ticket) {
self.tickets.push(ticket);
}
pub fn iter(&self) -> std::slice::Iter<Ticket> {
self.tickets.iter()
}
}
不会做了(
不知道代码怎么写,也没有示例说一下,要是找点别的内容搜一搜学一学应该就会做了吧,这两天没那么有干劲了,不太想折腾…唉
impl<'a> IntoIterator for &'a TicketStore {
type Item = &'a Ticket;
type IntoIter = std::slice::Iter<'a, Ticket>;
fn into_iter(self) -> Self::IntoIter {
self.tickets.iter()
}
}
这段代码视为TicketStroe
实现IntoIterator
,里面定义了Item
关联类型为&'a Ticket
,IntoIter
关联类型为std::slice::Iter<'a, Ticket>
。
然后就是一个into_iter
方法,参数是self,表示可以把TicketStore
转换为IntoIter
这个关联类型,也就是std::slice::Iter<'a, Ticket>
,对应的就是把里面的成员tickets
转换为了IntoIter
。
这一节的学习就先到这里吧,后面要好好复习一下生命周期,迭代器的实现这一块。