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::IterIter的定义是:

pub struct Iter<'a, T> { /* fields omitted */ }

image-20250923151218606

 

`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> {
        // [...]
    }
}

我只找到了这个:

image-20250923152657007

感觉好像差不太多……(很明显不是一个东西好吧)

 

好的,言归正传。

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 TicketIntoIter关联类型为std::slice::Iter<'a, Ticket>

然后就是一个into_iter方法,参数是self,表示可以把TicketStore转换为IntoIter这个关联类型,也就是std::slice::Iter<'a, Ticket>,对应的就是把里面的成员tickets转换为了IntoIter

 

这一节的学习就先到这里吧,后面要好好复习一下生命周期,迭代器的实现这一块。

阅读剩余
THE END