如何理解 rust 的单向链表

注:自我理解,详见rust丛书: 《rust语言圣经》

这里我们简单来实现一个单向链表。

单向链表(单链表)是链表的一种,其特点是链表的链接方向是单向的,对链表的访问要通过顺序读取从头部开始;链表是使用指针进行构造的列表;又称为结点列表,因为链表是由一个个结点组装起来的;其中每个结点都有指针成员变量指向列表中的下一个结点;

链表是由结点构成,head指针指向第一个成为表头结点,而终止于最后一个指向NULL的指针。

因此取一个单项链表的元素的时间复杂度是On,但是取最后一个元素的时间复杂度是O1。

然而链表有特殊的优点就是,插入数据快,可以随意插入到中间节点(数组的插入往往涉及到后面元素的拷贝,如果超过容量长度也会涉及到拷贝),同理,删除也是非常快,只需要将上一个链接的头跳过某个元素指向下一个,这就实现了删除,等等

正常来说,我们会有以下变量: 链表对象List,头:Head,节点:Node

一个List,有一个Head,Head指向一个Node,为了形成一个链,这个Node,还会指向下一个Node
由于链表中是需要保存数据的,因此数据是需要保存在节点中的,因此节点Node中不仅包含下一个元素next,还包含数据本身value(这里我们就只存一个数字,比如:i32,未来可以做成一个泛型T或者使用trait bound保存更多更丰富的数据)

好的,那就开始:

  1. 第一步定义节点对象:Node
// 首先准备一个Node对象
// struct Node {
//     value: i32,
//     next: ??,
// }
// 这个next需要指向下一个Node,因此next
// struct Node {
//     value: i32,
//     next: Node,
// }
// 这么写会发现,会报错:recursive type `Node` has infinite size
// 提示你Node外面要包一层比如Box,也就是智能指针,这样存储就不会导致无法计算需要多少内存了,
// 因为指针在栈上的内存确定的,就是8个字节(64位系统)
struct Node {
    value: i32,
    next: Box<Node>,
}
// 接下来你想想,这个next有2中可能,一种是存在一个Node,一个是不存在一个Node,rust中是不允许指向一个空的
// 因此很容易,外面再包一层Option即可,有值就是Node,没值就是None,这很容易理解的

因此,最终Node的定义如下:

struct Node {
    value: i32,
    next: Option<Box<Node>>,
}
  1. 第二步,定义链表:List
// struct List {
//     head: Node,
// }
// 看到上面定义的List,你是不是突然想起来,刚刚定义Node的时候,指向下一个Node,要包2层东西
// 一层用来确定指向的Node不是空,一层是用来指向Node本身的指针,也就是一个取Point
// 因此我们这里也同样的包一下,原因还是和上面的一样
struct List {
    head: Option<Box<Node>>,
}
// 因为我们知道,rust中是可以将某个长类型提取出来的,比如:type Next = Option<Box<Node>>;

因此最终定义如下:

type Next = Option<Box<Node>>;

struct List {
	size: u32, // 这里是为了配合查询链表长度添加的字段,如果没有这个功能,可以不要该属性
    head: Next,
}

struct Node {
    value: i32,
    next: Next,
}

好了,这样对象组成就定义完成了,接下来就是给这个List对象实现一些方法了,
需要实现的方法:

1. 构造方法new()
2. 展示列表show()
3. 添加元素方法push()
4. 删除尾部元素方法pop()
5. 搜索节点查询某个值get()
6. 通过某个值遍历列表删除某个值(节点)remove()
7. 链表长度len(),如果需要记录链表长度,那么List对象还需要添加一个size属性,即:
struct List {
    size: u32,
    head: Next,
}
8. 取最上面一个节点:pick()
9. 用于遍历查询下一个元素:next()
。。。。

需要知道,链表元素push是往头(第一个)元素上添加,pop也是从头取出删除,就和栈一样,先入后出。
具体的过程就是:首先创建一个节点node,将新Node的next指向原head指向的地方,最后将List的head指向新的Node

impl List {
    fn new() -> Self {
        List{
            // 给头添加一个空元素,空元素里面是没值的,这样就有一个头了
            head: Some(Box::new(Node { value: 0, next: None })),
        }
    }

    fn push(&mut self, value: i32) {
        // 创建node对象,需要了解的是,当插入的时候,需要将这个节点的下一个元素指向原来List的头,这样才是在头部插入
        let new_node = Box::new(Node{ value, next: self.head });
        // 将self.head重新指向该节点
        self.head = Some(new_node);
    }
}

很简单是吧,但是很不好意思,这是rust,你这么写是不会通过编译的,因为所有权问题:self.head是不允许移动的
那咋办呢,那我直接写成这样就不成了:
let new_node = Box::new(Node{ value, next: None });
那么问题来了,可以通过编译,但是如果你插入该节点时,这个List已经有元素了,就会导致之前的元素就没了,因为你next为None了呀,所以,还是必须要通过更改指针来解决。
而Option有一个方法:take(Takes the value out of the option, leaving a None in its place)
就是从该Option中取出Some(?)值,take完事了这时self.head就是None了,你就会想,这不还是放进去一个None了吗,但是你想想,在take的时候,我们把之前的head拿到了,并且把值交给了new_node,而不是和刚刚上面编译报错的例子一样直接丢弃了。
注,rust语言圣经用的是一个replace方法,一样,就是把self.head数据取出来,然后换成一个空,然后模式匹配取出来的这个Option,如果是Some就说明链表原来有值,现在要插入新值,因此要指针重新指定(a),而如果是None说明原来没值,当前节点时一个节点,这样啥都不要弄了
而这里使用take方法就比replace优雅的多,因为直接取出放进去即可,不用关心取出来的是不是一个None,反正如果是None,就说明是第一个元素呗。

第一阶段代码(仅包含push ,pop, 和一些peak方法,其中 show方法只是用来检查数据的,实际这么打印是没有任何意义的):

type Next = Option<Box<Node>>;

#[derive(Debug)]
struct List {
    size: u32,
    head: Next,
}

#[derive(Debug, PartialEq)]
struct Node {
    value: i32,
    next: Next,
}

impl List {
    fn new() -> Self {
        List {
            size: 0,
            // 给头添加一个空元素,空元素里面是没值的,这样就有一个头了
            head: Some(Box::new(Node { value: 0, next: None })),
        }
    }

    fn show(&self) {
        println!("list is {:?}", self);
    }

    fn push(&mut self, value: i32) {
        // 创建node对象,需要了解的是,当插入的时候,需要将这个节点的下一个元素指向原来List的头,这样才是在头部插入
        // Option.take()的方法描述的很清楚,取出Option中的Some(?),然后把原来的对象设置为None
        let new_node = Box::new(Node { value, next: self.head.take() });
        // 这时self.head就是空了,然后重新赋值,将self.head指向该节点
        assert_eq!(self.head, None);
        self.head = Some(new_node);
        // 此时是添加操作,因此元素长度+1
        self.size += 1;
    }

    fn pop(&mut self) -> Option<i32> {
        if self.size == 0 {
            panic!("no value to pop.")
        }

        // 使用match匹配
        // match self.head.take() {
        //     Some(node) => {
        //         self.head = node.next;
        //         self.size -= 1;
        //         Some(node.value)
        //     },
        //     None => None,
        // }

        //  由于self.size == 0的情况已经考虑,因此就不需要match或if let的模式匹配 ,直接let
        let node = self.head.take().unwrap();
        self.head = node.next;
        self.size -= 1;
        Some(node.value)
    }

    fn peek(&self) -> Option<&i32> {
        if self.size == 0 {
            return None;
        }
        // map可以从一个Option中取到里面的数据,然后操作后再返回给Option进行包装
        // 就是他描述的:Maps an Option<T> to Option<U> by applying a function to a contained value.
        self.head.as_ref().map(|node| {
            &node.value
        })
    }
    fn peek_by_unwrap(&self) -> Option<&i32> {
        if self.size == 0 {
            return None;
        }
        // 这种方法比较直接,先用unwrap打开Option,获取值value后,再用Some包起来,一样
        return Some(&self.head.as_ref().unwrap().value);
    }

    // 可取出并改值
    fn peek_mut(&mut self) -> Option<&mut i32> {
        if self.size == 0 {
            return None;
        }
        self.head.as_mut().map(|node| {
            &mut node.value
        })
    }

    fn peek_and_update(&mut self, new_value: i32) {
        if self.size == 0 {
            return ();
        }
        self.head.as_mut().map(|node| {
            node.value = new_value;
        });
    }
}

// 这是从rust圣经看到的,然后把replace换成take
impl Drop for List {
    fn drop(&mut self) {
        let mut cur_node = self.head.take();

        while let Some(mut temp_node) = cur_node {
            println!("---> dropping node --->");
            cur_node = temp_node.next.take();
            // temp_node 在这里超出作用域并被 drop,
            // 由于它的 `next` 字段拥有的 `Node` 被设置为 None,
            // 因此这里并不会有无边界的递归发生
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn basics() {
        let mut list = List::new();
        // Populate list
        list.push(1);
        list.show();
        list.push(2);
        list.show();
        list.push(3);
        list.show();
        // Check normal removal
        assert_eq!(list.pop(), Some(3));
        assert_eq!(list.pop(), Some(2));
        list.show();
    }

    #[test]
    fn peek() {
        let mut list = List::new();
        assert_eq!(list.peek(), None);
        assert_eq!(list.peek_mut(), None);
        list.push(1);
        list.push(2);
        list.push(3);

        assert_eq!(list.peek(), Some(&3));
        assert_eq!(list.peek_mut(), Some(&mut 3));
        list.peek_and_update(4);
        assert_eq!(list.peek(), Some(&4));

        list.peek_mut().map(|value| {
            *value = 42
        });

        assert_eq!(list.peek(), Some(&42));
        assert_eq!(list.pop(), Some(42));
    }
}

此时真正的遍历查询所有节点的功能还没有

改进: 接下来就是将原来的i32换成泛型T

type Next<T> = Option<Box<Node<T>>>;

#[derive(Debug)]
struct List<T> {
    size: u32,
    head: Next<T>,
}

#[derive(Debug, PartialEq)]
struct Node<T> {
    value: T,
    next: Next<T>,
}


impl<T> List<T> {
    fn new() -> Self {
        List {
            size: 0,
            // 给头添加一个空元素,空元素里面是没值的,这样就有一个头了
            head: None,
        }
    }

    fn push(&mut self, value: T) {
        // 创建node对象,需要了解的是,当插入的时候,需要将这个节点的下一个元素指向原来List的头,这样才是在头部插入
        // Option.take()的方法描述的很清楚,取出Option中的Some(?),然后把原来的对象设置为None
        let new_node = Box::new(Node { value, next: self.head.take() });
        self.head = Some(new_node);
        // 此时是添加操作,因此元素长度+1
        self.size += 1;
    }

    fn pop(&mut self) -> Option<T> {
        if self.size == 0 {
            panic!("no value to pop.")
        }

        //  由于self.size == 0的情况已经考虑,因此就不需要match或if let的模式匹配 ,直接let
        let node = self.head.take().unwrap();
        self.head = node.next;
        self.size -= 1;
        Some(node.value)
    }

    fn peek(&self) -> Option<&T> {
        if self.size == 0 {
            return None;
        }
        // map可以从一个Option中取到里面的数据,然后操作后再返回给Option进行包装
        // 就是他描述的:Maps an Option<T> to Option<U> by applying a function to a contained value.
        self.head.as_ref().map(|node| {
            &node.value
        })
    }
    fn peek_by_unwrap(&self) -> Option<&T> {
        if self.size == 0 {
            return None;
        }
        // 这种方法比较直接,先用unwrap打开Option,获取值value后,再用Some包起来,一样
        return Some(&self.head.as_ref().unwrap().value);
    }

    // 可取出并改值
    fn peek_mut(&mut self) -> Option<&mut T> {
        if self.size == 0 {
            return None;
        }
        self.head.as_mut().map(|node| {
            &mut node.value
        })
    }

    fn peek_and_update(&mut self, new_value: T) {
        if self.size == 0 {
           return ();
        }
        self.head.as_mut().map(|node| {
            node.value = new_value;
        });
    }
}

impl<T> Drop for List<T> {
    fn drop(&mut self) {
        let mut cur_node = self.head.take();

        while let Some(mut temp_node) = cur_node {
            println!("---> dropping node --->");
            cur_node = temp_node.next.take();
            // temp_node 在这里超出作用域并被 drop,
            // 由于它的 `next` 字段拥有的 `Node` 被设置为 None,
            // 因此这里并不会有无边界的递归发生
        }
    }
}

为了实现遍历功能,将该链表(集合类型)转化成可迭代功能

实现一个迭代器,可以通过实现3个trait:1.对象所有权IntoIter ,2.对象可变引用IterMut ,3.对象不可变引用Iter

毫无疑问,我们实际上想要取的是第三个不可变的引用,因为,我们不能说遍历完链表,链表就没了,而获取所有权的迭代器会把链表消耗掉的,因为要把链表所有权转给迭代器,然后迭代器进行消耗,这就没意义了。

如果我们想要通过迭代并实现元素的更新,那就必须实现对象可变的引用,这种trait对吧,原因很简单,你取到了对该链表的引用,然后当你想要修改链表的时候(不管是插入数据,删除数据,还是更新数据)都是要做mut操作的。

通过上面的分析,我们知道,最终目的是实现Iter和IterMut,但是通过学习rust我们知道,最简单的是获取所有权,获取所有权是不需要分析生命周期的,而rust的生命周期又是仅次于unsafe难度的坑(我个人理解),因此简单入手,逐渐增加难度才是作为一个新rustacean能做的。

part1.实现获取所有权类型的迭代器:IntoIter.
// 首先定义trait,只要实现这个trait就可以进行next操作进行下一个元素的迭代,因此要有一个next方法
// 这里还用了关联类型,实际上使用泛型也可以,不过泛型有他的局限性,并且每个实现它的对象都要重写泛型的定义(具体原因见rust圣经)
pub trait Iterator {
    type Item; // 这里的 Item 是关联类型,用于在trait中指代某个类型
    fn next(&mut self) -> Option<Self::Item>; // 由于next最后一个为None,因此元素用Option包裹一层
}

// 创建一个元祖结构体IntoIter (List)
pub struct IntoIter<T> (List<T>);
// 给IntoIter实现Iterator 的trait,也就是重载next方法
impl<T> Iterator for IntoIter<T> {
    type Item = T;

    fn next(&mut self) -> Option<Self::Item> {
        // 从元组中取出第一个元素就是List了
        self.0.pop()
    }
}

impl<T> List<T> {
    // type: List<T> ---> (List<T>>)
    pub fn into_iter(self) -> IntoIter<T> {
        IntoIter(self)
    }
}

// 测试部分(注意,上面只写了迭代器部分的代码,需要将上面的代码放到原来的代码中方可)
#[test]
    fn into_iter() {
        let mut list = List::new();
        list.push(1);
        list.push(2);
        list.push(3);
        let mut iter = list.into_iter();
        assert_eq!(iter.next(), Some(3));
        assert_eq!(iter.next(), Some(2));
        assert_eq!(iter.next(), Some(1));
        // assert_eq!(iter.next(), None);
    }
part2, 实现不可变的引用迭代器: Iter

要实现这个不可变的引用,我们不能想part1一样,获取链表List,然后用List的pop方法了,那我们就必须要创建一个能存储一个节点指针信息的对象,获取该节点,并且通过该节点的next属性,查到下一个节点的指针对象,之所以是指针对象,是因为我们是要引用(&)节点,才能不影响对象的移动。

这里会有一个新的api:as_deref: 可以将Option的数据取出来,然后返回一个里面对象的引用
在这里插入图片描述

// 通过上面的分析,我们知道这个Iter对象不能和之前的IntoIter一样,它应该是一个有属性的结构体,因为他要存放节点的指针信息,用于展示
// pub struct Iter<T> {
//     info: Option<&Node<T>>,
// }

// 这么写之后,编译会爆:Option<&Node<T>>中的type `T` may not live long enough,也就是我们最早分析的,这里要涉及到生命周期的指定
// 编译器报错也会提示,是不是要考虑给&Node<T>加一个'a的生命周期参数
// 原因就是struct中的元素如果是引用类型的,那就必须要求这个引用的生命长于这个struct,否则这个指针可能会悬垂
// pub struct Iter<'a, T> {
//     info: Option<&'a Node<T>>,
// }

// 接下来就是要给他实现之前定义的iterator的trait
// impl<'a, T>Iterator for Iter<'a, T> {
//     type Item = &T;
//     
//     // 这里的self是Node节点的引用,因此是&self,而又因为这个next要随着取出下一个换掉,因此还要是mut类型
//     fn next(&mut self) -> Option<Self::Item> {
//         todo!()
//     }
// }

// 这里Item要指定是T类型的指针,因为定义好的next返回的是一个Option<&Node<T>>
// 但是当这么修改后:type Item = &T;会报错缺少生命参数,因此最后改成:type Item = &'a T;
// pub struct Iter<'a, T> {
//     info: Option<&'a Node<T>>,
// }
// 
// impl<'a, T>Iterator for Iter<'a, T> {
//     type Item = &'a T;
// 
//     // 这里的self要知道是Node节点的引用,因此是&self,而又因为这个next要随着取出下一个换掉,因此还要是mut类型
//     fn next(&mut self) -> Option<Self::Item> {
//         self.info.map(|node|{
//             // 将临时变量node中的数据返回,展示出去
//             &node.value
//         })
//     }
// }
// 上面在定义next时候,直接把node的value返回回去之后,是不是发现Iter的info没有指向下一个元素,这样就没办法再次next了
// 因此在返回前还需要加一行内容,将self.info指向下一个node的指针:self.info = node.next.as_deref();
// 最终代码:
pub struct Iter<'a, T> {
    info: Option<&'a Node<T>>,
}

impl<'a, T>Iterator for Iter<'a, T> {
    type Item = &'a T;

    // 这里的self要知道是Node节点的引用,因此是&self,而又因为这个next要随着取出下一个换掉,因此还要是mut类型
    fn next(&mut self) -> Option<Self::Item> {
        self.info.map(|node|{
            // 将self.info换成下一个节点数据,node.next是一个Option类型,而Option.as_deref()可以获取定义关联类型的值的引用
            // 相当于多去掉一层
            self.info = node.next.as_deref();
            // 将临时变量node中的数据返回,展示出去
            &node.value
        })
    }
}
// 接下来再给List实现iter方法,将List转成Iter


// 测试部分
    #[test]
    fn iter() {
        let mut list = List::new();
        list.push(1);
        list.push(2);
        list.push(3);
    
        let mut iter = list.iter();
        assert_eq!(iter.next(), Some(&3));
        assert_eq!(iter.next(), Some(&2));
        assert_eq!(iter.next(), Some(&1));
        assert_eq!(list.pop(), Some(3)); // 因为是引用迭代,因此并不影响源List可以pop()数据
        assert_eq!(list.pop(), Some(2));
        assert_eq!(list.pop(), Some(1));
    }

part3: 可变引用: IterMut

分析和part2一样,只不过这个变成了mut类型,因此,只需要按部就班的和part2一样,现将struct创建出来,多加一个mut即可。然后按照编译器的要求添加相应的生命周期参数

pub struct MutIter<'a, T> {
// 因为这里查到的Node不仅要用来显示,而且还可以外部修改,因此把原来的Option<&'a Node<T>> --> Option<&'a mut Node<T>>
    info: Option<&'a mut Node<T>>,
}

impl<'a, T> Iterator for MutIter<'a, T> {
    type Item = &'a T; // 最后这里也要改成mut的: type Item = &'a mut T;

    fn next(&mut self) -> Option<Self::Item> {
		// 实际这么写是会报错的,因为这里的self.info中的&mut T是不可Copy的,因此这里就会move,
		// cannot move out of `self.info` which is behind a mutable reference,因为可变的引用self压根不拥有info
		// 那肯定就更不可能move info了,因此这里使用take(),将数据临时取出再放进去一个空
        self.info.map(|node|{
            self.info = node.next.as_deref_mut();
            &node.value // 当然这里最后也必须是mut的,不然返回的数据不可修改: &mut node.value 
        })
    }
}

impl<T> List<T> {
    pub fn iter_mut(&mut self) -> IterMut<T> {
    	// 很清楚,这个和as_deref()方法返回的值一样,不过这个值是mut的
        IterMut{ info: self.head.as_deref_mut() }
    }
}


注: 为什么可变的引用(&mut T)不可以Copy,因为一个引用说白了就是一个指针,拷贝一个指针就是多一个指向某值的拷贝,而如果这个值是可变的,那很可能它在某次变化后它的地址就变了(比如vec元素增加的时候,超过cap值进行扩容,此时它会选一块新的内存地址,将原来的数据拷贝过去),这样就会产生悬垂引用,即一个指向无效内存的指针

最终part3完整的样子:

pub struct IterMut<'a, T> {
    info: Option<&'a mut Node<T>>,
}

impl<'a, T> Iterator for IterMut<'a, T> {
    type Item = &'a mut T;

    fn next(&mut self) -> Option<Self::Item> {
        // 由于self.info中的&mut T是没有满足copy的trait,因此这里就会move,但是我们又不能move这个值
        // 这里使用self.info.take()将self.info临时拿出来,然后放进去一个None,然后在对拿出来的数据做map操作
        // 不用担心self.info变成None会指向空,因为里面会重新把下一个元素取出来放到这个self.info中
        self.info.take().map(|node| {
            self.info = node.next.as_deref_mut();
            &mut node.value
        })
    }
}

impl<T> List<T> {
    pub fn iter_mut(&mut self) -> IterMut<T> {
        IterMut{ info: self.head.as_deref_mut() }
    }
}

// 测试代码
#[test]
fn iter_mut() {
    let mut list = List::new();
    list.push(1); list.push(2); list.push(3);

    let mut iter = list.iter_mut();
    assert_eq!(iter.next(), Some(&mut 3));
    assert_eq!(iter.next(), Some(&mut 2));
    assert_eq!(iter.next(), Some(&mut 1));
}

最终的代码

type Next<T> = Option<Box<Node<T>>>;

#[derive(Debug)]
struct List<T> {
    size: u32,
    head: Next<T>,
}

#[derive(Debug, PartialEq)]
struct Node<T> {
    value: T,
    next: Next<T>,
}


impl<T> List<T> {
    fn new() -> Self {
        List {
            size: 0,
            // 给头添加一个空元素,空元素里面是没值的,这样就有一个头了
            head: None,
        }
    }

    fn push(&mut self, value: T) {
        // 创建node对象,需要了解的是,当插入的时候,需要将这个节点的下一个元素指向原来List的头,这样才是在头部插入
        // Option.take()的方法描述的很清楚,取出Option中的Some(?),然后把原来的对象设置为None
        let new_node = Box::new(Node { value, next: self.head.take() });
        self.head = Some(new_node);
        // 此时是添加操作,因此元素长度+1
        self.size += 1;
    }

    fn pop(&mut self) -> Option<T> {
        if self.size == 0 {
            panic!("no value to pop.")
        }

        //  由于self.size == 0的情况已经考虑,因此就不需要match或if let的模式匹配 ,直接let
        let node = self.head.take().unwrap();
        self.head = node.next;
        self.size -= 1;
        Some(node.value)
    }

    fn peek(&self) -> Option<&T> {
        if self.size == 0 {
            return None;
        }
        // map可以从一个Option中取到里面的数据,然后操作后再返回给Option进行包装
        // 就是他描述的:Maps an Option<T> to Option<U> by applying a function to a contained value.
        self.head.as_ref().map(|node| {
            &node.value
        })
    }
    fn peek_by_unwrap(&self) -> Option<&T> {
        if self.size == 0 {
            return None;
        }
        // 这种方法比较直接,先用unwrap打开Option,获取值value后,再用Some包起来,一样
        return Some(&self.head.as_ref().unwrap().value);
    }

    // 可取出并改值
    fn peek_mut(&mut self) -> Option<&mut T> {
        if self.size == 0 {
            return None;
        }
        self.head.as_mut().map(|node| {
            &mut node.value
        })
    }

    fn peek_and_update(&mut self, new_value: T) {
        if self.size == 0 {
            return ();
        }
        self.head.as_mut().map(|node| {
            node.value = new_value;
        });
    }
    // 由于实现了iter类型,即不可变引用,因此满足通过show链表数据,因为T类型并没有bind debug trait,因此没办法在里面直接打印
    // 只能直接把迭代器返回出去得了,然后在外面遍历打印
    fn show(&self) -> Iter<T> {
        self.iter()
    }
}

impl<T> Drop for List<T> {
    fn drop(&mut self) {
        let mut cur_node = self.head.take();

        while let Some(mut temp_node) = cur_node {
            println!("---> dropping node --->");
            cur_node = temp_node.next.take();
            // temp_node 在这里超出作用域并被 drop,
            // 由于它的 `next` 字段拥有的 `Node` 被设置为 None,
            // 因此这里并不会有无边界的递归发生
        }
    }
}

// 集合类型可以通过 Iterator 特征进行迭代
// 准备一个trait(特征),包含一个next方法,调用的时候就会取出下一个元素
pub trait Iterator {
    type Item;
    // 这里的 Item 是关联类型,用于在trait中指代某个类型
    fn next(&mut self) -> Option<Self::Item>;
}

// 实现一个迭代器,需要实现3个trait,对象所有权IntoIter ,对象可变引用IterMut ,对象不可变引用Iter
// 创建一个元祖结构体IntoIter (List)
pub struct IntoIter<T> (List<T>);

impl<T> Iterator for IntoIter<T> {
    type Item = T;

    fn next(&mut self) -> Option<Self::Item> {
        // 从元组中取出第一个元素就是List了
        self.0.pop()
    }
}

impl<T> List<T> {
    // type: List<T> ---> (List<T>>)
    pub fn into_iter(self) -> IntoIter<T> {
        IntoIter(self)
    }
}

pub struct Iter<'a, T> {
    info: Option<&'a Node<T>>,
}

impl<'a, T>Iterator for Iter<'a, T> {
    type Item = &'a T;

    // 这里的self要知道是Node节点的引用,因此是&self,而又因为这个next要随着取出下一个换掉,因此还要是mut类型
    fn next(&mut self) -> Option<Self::Item> {
        self.info.map(|node|{
            // 将self.info换成下一个节点数据
            self.info = node.next.as_deref();
            // 将临时变量node中的数据返回,展示出去
            &node.value
        })
    }
}
impl<T> List<T> {
    pub fn iter(&self) -> Iter<T> {
        Iter{ info: self.head.as_deref() }
    }
}

pub struct IterMut<'a, T> {
    info: Option<&'a mut Node<T>>,
}

impl<'a, T> Iterator for IterMut<'a, T> {
    type Item = &'a mut T;

    fn next(&mut self) -> Option<Self::Item> {
        // 由于self.info中的&mut T是没有满足copy的trait,因此这里就会move,但是我们又不能move这个值
        // 这里使用self.info.take()将self.info临时拿出来,然后放进去一个None,然后在对拿出来的数据做map操作
        // 不用担心self.info变成None会指向空,因为里面会重新把下一个元素取出来放到这个self.info中
        self.info.take().map(|node| {
            self.info = node.next.as_deref_mut();
            &mut node.value
        })
    }
}

impl<T> List<T> {
    pub fn iter_mut(&mut self) -> IterMut<T> {
        IterMut{ info: self.head.as_deref_mut() }
    }
}



#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn basics2() {
        let mut list = List::new();
        // Populate list
        list.push(1);
        list.push(2);
        list.push(3);
        // Check normal removal
        assert_eq!(list.pop(), Some(3));
        assert_eq!(list.pop(), Some(2));
    }

    #[test]
    fn peek2() {
        let mut list = List::new();
        assert_eq!(list.peek(), None);
        assert_eq!(list.peek_mut(), None);
        list.push(1);
        list.push(2);
        list.push(3);

        assert_eq!(list.peek(), Some(&3));
        assert_eq!(list.peek_mut(), Some(&mut 3));
        list.peek_and_update(4);
        assert_eq!(list.peek(), Some(&4));

        list.peek_mut().map(|value| {
            *value = 42
        });

        assert_eq!(list.peek(), Some(&42));
        assert_eq!(list.pop(), Some(42));
    }

    #[test]
    fn into_iter() {
        let mut list = List::new();
        list.push(1);
        list.push(2);
        list.push(3);
        let mut iter = list.into_iter();
        assert_eq!(iter.next(), Some(3));
        assert_eq!(iter.next(), Some(2));
        assert_eq!(iter.next(), Some(1));
        // assert_eq!(iter.next(), None);
    }

    #[test]
    fn iter() {
        let mut list = List::new();
        list.push(1);
        list.push(2);
        list.push(3);

        let mut iter = list.iter();
        assert_eq!(iter.next(), Some(&3));
        assert_eq!(iter.next(), Some(&2));
        assert_eq!(iter.next(), Some(&1));
        assert_eq!(list.pop(), Some(3));
        assert_eq!(list.pop(), Some(2));
        assert_eq!(list.pop(), Some(1));
    }

    #[test]
    fn iter_mut() {
        let mut list = List::new();
        list.push(1); list.push(2); list.push(3);

        let mut iter = list.iter_mut();
        assert_eq!(iter.next(), Some(&mut 3));
        assert_eq!(iter.next(), Some(&mut 2));
        assert_eq!(iter.next(), Some(&mut 1));
    }
    #[test]
    fn show_list() {
        let mut list = List::new();
        list.push(1);
        list.push(2);
        list.push(3);
        let mut iter = list.show();
        // let mut size = list.size;
        // while size > 0 {
        //     let node = iter.next();
        //     size -= 1;
        //     println!("node: {:?}", node);
        // }
        // 直接使用while let进行循环匹配更加优雅
        while let Some(node) = iter.next() {
            println!("node value: {:?}", node);
        }
    }
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值