Drop、Arc 及完整代码

Drop

与之前链表存在的问题相似,新的链表也有递归的问题。下面是之前的解决方法:

#![allow(unused)]
fn main() {
impl<T> Drop for List<T> {
    fn drop(&mut self) {
        let mut cur_link = self.head.take();
        while let Some(mut boxed_node) = cur_link {
            cur_link = boxed_node.next.take();
        }
    }
}
}

但是 boxed_node.next.take() 的方式在新的链表中无法使用,因为我们没办法去修改 Rc 持有的值。

考虑一下相关的逻辑,可以发现,如果当前的节点仅被当前链表所引用(Rc 的引用计数为 1),那该节点是可以安全 drop 的:

#![allow(unused)]
fn main() {
impl<T> Drop for List<T> {
    fn drop(&mut self) {
        let mut head = self.head.take();
        while let Some(node) = head {
            if let Ok(mut node) = Rc::try_unwrap(node) {
                head = node.next.take();
            } else {
                break;
            }
        }
    }
}
}

这里有一个没见过的方法 Rc::Try_unwrap ,该方法会判断当前的 Rc 是否只有一个强引用,若是,则返回 Rc 持有的值,否则返回一个错误。

可以看出,我们会一直 drop 到第一个被其它链表所引用的节点:

list1 -> A ---+
              |
              v
list2 ------> B -> C -> D
              ^
              |
list3 -> X ---+

例如如果要 drop List2,那会从头节点开始一直 drop 到 B 节点时停止,剩余的 B -> C -> D 三个节点由于引用计数不为 1 (同时被多个链表引用) ,因此不会被 drop。

测试下新的代码:

$ cargo test

   Compiling lists v0.1.0 (/Users/ABeingessner/dev/too-many-lists/lists)
    Finished dev [unoptimized + debuginfo] target(s) in 1.10s
     Running /Users/ABeingessner/dev/too-many-lists/lists/target/debug/deps/lists-86544f1d97438f1f

running 8 tests
test first::test::basics ... ok
test second::test::basics ... ok
test second::test::into_iter ... ok
test second::test::iter ... ok
test second::test::iter_mut ... ok
test second::test::peek ... ok
test third::test::basics ... ok
test third::test::iter ... ok

test result: ok. 8 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

完美通过,下面再来考虑一个问题,如果我们的链表要在多线程环境使用该怎么办?

Arc

不可变链表的一个很大的好处就在于多线程访问时自带安全性,毕竟共享可变性是多线程危险的源泉,最好也是最简单的解决办法就是直接干掉可变性。

但是 Rc<T> 本身并不是线程安全的,原因在之前的章节也有讲:它内部的引用计数器并不是线程安全的,通俗来讲,计数器没有加锁也没有实现原子性。

再结合之前章节学过的内容,绝大部分同学应该都能想到, Arc<T> 就是我们的最终答案。

那么还有一个问题,我们怎么知道一个类型是不是类型安全?会不会在多线程误用了非线程安全的类型呢?这就是 Rust 安全性的另一个强大之处:Rust 通过提供 SendSync 两个特征来保证线程安全。

关于 SendSync 的详细介绍,请参见此章节

完整代码

又到了喜闻乐见的环节,新链表的代码相比之前反而还更简单了,不可变就是香!

#![allow(unused)]
fn main() {
use std::rc::Rc;

pub struct List<T> {
    head: Link<T>,
}

type Link<T> = Option<Rc<Node<T>>>;

struct Node<T> {
    elem: T,
    next: Link<T>,
}

impl<T> List<T> {
    pub fn new() -> Self {
        List { head: None }
    }

    pub fn prepend(&self, elem: T) -> List<T> {
        List { head: Some(Rc::new(Node {
            elem: elem,
            next: self.head.clone(),
        }))}
    }

    pub fn tail(&self) -> List<T> {
        List { head: self.head.as_ref().and_then(|node| node.next.clone()) }
    }

    pub fn head(&self) -> Option<&T> {
        self.head.as_ref().map(|node| &node.elem)
    }

    pub fn iter(&self) -> Iter<'_, T> {
        Iter { next: self.head.as_deref() }
    }
}

impl<T> Drop for List<T> {
    fn drop(&mut self) {
        let mut head = self.head.take();
        while let Some(node) = head {
            if let Ok(mut node) = Rc::try_unwrap(node) {
                head = node.next.take();
            } else {
                break;
            }
        }
    }
}

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

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

    fn next(&mut self) -> Option<Self::Item> {
        self.next.map(|node| {
            self.next = node.next.as_deref();
            &node.elem
        })
    }
}

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

    #[test]
    fn basics() {
        let list = List::new();
        assert_eq!(list.head(), None);

        let list = list.prepend(1).prepend(2).prepend(3);
        assert_eq!(list.head(), Some(&3));

        let list = list.tail();
        assert_eq!(list.head(), Some(&2));

        let list = list.tail();
        assert_eq!(list.head(), Some(&1));

        let list = list.tail();
        assert_eq!(list.head(), None);

        // Make sure empty tail works
        let list = list.tail();
        assert_eq!(list.head(), None);
    }

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

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