迭代器

坏男孩最令人头疼,而链表实现中,迭代器就是这样的坏男孩,所以我们放在最后来处理。

IntoIter

由于是转移所有权,因此 IntoIter 一直都是最好实现的:

#![allow(unused)]
fn main() {
pub struct IntoIter<T>(List<T>);

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

impl<T> Iterator for IntoIter<T> {
    type Item = T;
    fn next(&mut self) -> Option<T> {
        self.0.pop_front()
    }
}
}

但是关于双向链表,有一个有趣的事实,它不仅可以从前向后迭代,还能反过来。前面实现的是传统的从前到后,那问题来了,反过来该如何实现呢?

答案是: DoubleEndedIterator,它继承自 Iterator( 通过 supertrait ),因此意味着要实现该特征,首先需要实现 Iterator

这样只要为 DoubleEndedIterator 实现 next_back 方法,就可以支持双向迭代了: Iteratornext 方法从前往后,而 next_back 从后向前。

#![allow(unused)]
fn main() {
impl<T> DoubleEndedIterator for IntoIter<T> {
    fn next_back(&mut self) -> Option<T> {
        self.0.pop_back()
    }
}
}

测试下:

#![allow(unused)]
fn main() {
#[test]
fn into_iter() {
    let mut list = List::new();
    list.push_front(1); list.push_front(2); list.push_front(3);

    let mut iter = list.into_iter();
    assert_eq!(iter.next(), Some(3));
    assert_eq!(iter.next_back(), Some(1));
    assert_eq!(iter.next(), Some(2));
    assert_eq!(iter.next_back(), None);
    assert_eq!(iter.next(), None);
}
}
$ cargo test

     Running target/debug/lists-5c71138492ad4b4a

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

test result: ok. 11 passed; 0 failed; 0 ignored; 0 measured

Iter

这里又要用到糟糕的 Ref:

#![allow(unused)]
fn main() {
pub struct Iter<'a, T>(Option<Ref<'a, Node<T>>>);

impl<T> List<T> {
    pub fn iter(&self) -> Iter<T> {
        Iter(self.head.as_ref().map(|head| head.borrow()))
    }
}
}
$ cargo build

迄今为止一切运行正常,接下来的 next 实现起来会有些麻烦:

#![allow(unused)]
fn main() {
impl<'a, T> Iterator for Iter<'a, T> {
    type Item = Ref<'a, T>;
    fn next(&mut self) -> Option<Self::Item> {
        self.0.take().map(|node_ref| {
            self.0 = node_ref.next.as_ref().map(|head| head.borrow());
            Ref::map(node_ref, |node| &node.elem)
        })
    }
}
}
$ cargo build

error[E0521]: borrowed data escapes outside of closure
   --> src/fourth.rs:155:13
    |
153 |     fn next(&mut self) -> Option<Self::Item> {
    |             --------- `self` is declared here, outside of the closure body
154 |         self.0.take().map(|node_ref| {
155 |             self.0 = node_ref.next.as_ref().map(|head| head.borrow());
    |             ^^^^^^   -------- borrow is only valid in the closure body
    |             |
    |             reference to `node_ref` escapes the closure body here

error[E0505]: cannot move out of `node_ref` because it is borrowed
   --> src/fourth.rs:156:22
    |
153 |     fn next(&mut self) -> Option<Self::Item> {
    |             --------- lifetime `'1` appears in the type of `self`
154 |         self.0.take().map(|node_ref| {
155 |             self.0 = node_ref.next.as_ref().map(|head| head.borrow());
    |             ------   -------- borrow of `node_ref` occurs here
    |             |
    |             assignment requires that `node_ref` is borrowed for `'1`
156 |             Ref::map(node_ref, |node| &node.elem)
    |                      ^^^^^^^^ move out of `node_ref` occurs here

果然,膝盖又中了一箭。

node_ref 活得不够久,跟一般的引用不同,Rust 不允许我们这样分割 Ref,从 head.borrow() 中取出的 Ref 只允许跟 node_ref 活得一样久。

而我们想要的函数是存在的:

#![allow(unused)]
fn main() {
pub fn map_split<U, V, F>(orig: Ref<'b, T>, f: F) -> (Ref<'b, U>, Ref<'b, V>) where
    F: FnOnce(&T) -> (&U, &V),
    U: ?Sized,
    V: ?Sized,
}

喔,这个函数定义的泛型直接晃瞎了我的眼睛。。

#![allow(unused)]
fn main() {
fn next(&mut self) -> Option<Self::Item> {
    self.0.take().map(|node_ref| {
        let (next, elem) = Ref::map_split(node_ref, |node| {
            (&node.next, &node.elem)
        });

        self.0 = next.as_ref().map(|head| head.borrow());

        elem
    })
}
}
$ cargo build

   Compiling lists v0.1.0 (/Users/ABeingessner/dev/temp/lists)
error[E0521]: borrowed data escapes outside of closure
   --> src/fourth.rs:159:13
    |
153 |     fn next(&mut self) -> Option<Self::Item> {
    |             --------- `self` is declared here, outside of the closure body
...
159 |             self.0 = next.as_ref().map(|head| head.borrow());
    |             ^^^^^^   ---- borrow is only valid in the closure body
    |             |
    |             reference to `next` escapes the closure body here

额,借用的内容只允许在闭包体中使用,看起来我们还是得用 Ref::map 来解决问题:

#![allow(unused)]
fn main() {
fn next(&mut self) -> Option<Self::Item> {
    self.0.take().map(|node_ref| {
        let (next, elem) = Ref::map_split(node_ref, |node| {
            (&node.next, &node.elem)
        });

        self.0 = if next.is_some() {
            Some(Ref::map(next, |next| &**next.as_ref().unwrap()))
        } else {
            None
        };

        elem
    })
}
}
error[E0308]: mismatched types
   --> src/fourth.rs:162:22
    |
162 |                 Some(Ref::map(next, |next| &**next.as_ref().unwrap()))
    |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `fourth::Node`, found struct `std::cell::RefCell`
    |
    = note: expected type `std::cell::Ref<'_, fourth::Node<_>>`
               found type `std::cell::Ref<'_, std::cell::RefCell<fourth::Node<_>>>`

晕, 多了一个 RefCell ,随着我们的对链表的逐步深入,RefCell 的代码嵌套变成了不可忽视的问题。

看起来我们已经无能为力了,只能试着去摆脱 RefCell 了。Rc 怎么样?我们完全可以对 Rc 进行完整的克隆:

#![allow(unused)]
fn main() {
pub struct Iter<T>(Option<Rc<Node<T>>>);

impl<T> List<T> {
    pub fn iter(&self) -> Iter<T> {
        Iter(self.head.as_ref().map(|head| head.clone()))
    }
}

impl<T> Iterator for Iter<T> {
    type Item =
}

等等,那现在返回的是什么?&T 还是 Ref<T> ?

两者都不是,现在我们的 Iter 已经没有生命周期了:无论是 &T 还是 Ref<T> 都需要我们在 next 之前声明好生命周期。但是我们试图从 Rc 中取出来的值其实是迭代器的引用。

也可以通过对 Rc 进行 map 获取到 Rc<T>?但是标准库并没有给我们提供相应的功能,第三方倒是有一个

但是,即使这么做了,还有一个更大的坑在等着:一个会造成迭代器不合法的可怕幽灵。事实上,之前我们对于迭代器不合法是免疫的,但是一旦迭代器产生 Rc,那它们就不再会借用链表。这意味着人们可以在持有指向链表内部的指针时,还可以进行 pushpop 操作。

严格来说,push 问题不大,因为链表两端的增长不会对我们正在关注的某个子链表造成影响。

但是 pop 就是另一个故事了,如果在我们关注的子链表之外 pop, 那问题不大。但是如果是 pop 一个正在引用的子链表中的节点呢?那一切就完了,特别是,如果大家还试图去 unwrap try_unwrap 返回的 Result ,会直接造成整个程序的 panic

仔细想一想,好像也不错,程序一切正常,除非去 pop 我们正在引用的节点,最美的是,就算遇到这种情况,程序也会直接崩溃,提示我们错误的发生。

其实我们大部分的努力都是为了实现隐藏的细节和优雅的 API,典型的二八原则,八成时间花在二成的细节上。但是如果不关心这些细节,可以接受自己的平凡的话,那把节点简单的到处传递就行。

总之,可以看出,内部可变性非常适合写一个安全性的应用程序,但是如果是安全性高的库,那内部可变性就有些捉襟见肘了。

最终,我选择了放弃,不再实现 IterIterMut,也许努力下,可以实现,但是。。。不愉快,算了。