Option

特别细心的读者可能已经注意到,我们实际上重新发明了一个非常糟糕的版本的 Option:

enum Link {
    Empty,
    More(Box<Node>),
}

Link 仅仅是 Option<Box<node>> 。现在,不必在任何地方编写 Option<Box<Node> 真是太好了,而且不像 pop,我们没有向外部世界暴露这个Option,所以可能没什么问题。然而,Option 有一些非常好的方法,我们一直在手动实现,让我们不要这样做,而应将所有内容替换为 Option。首先,我们将很天真地将所有内容重命名为 Some 和 None:

use std::mem;

pub struct List {
    head: Link,
}

// yay type aliases!
type Link = Option<Box<Node>>;

struct Node {
    elem: i32,
    next: Link,
}

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

    pub fn push(&mut self, elem: i32) {
        let new_node = Box::new(Node {
            elem: elem,
            next: mem::replace(&mut self.head, None),
        });

        self.head = Some(new_node);
    }

    pub fn pop(&mut self) -> Option<i32> {
        match mem::replace(&mut self.head, None) {
            None => None,
            Some(node) => {
                self.head = node.next;
                Some(node.elem)
            }
        }
    }
}

impl Drop for List {
    fn drop(&mut self) {
        let mut cur_link = mem::replace(&mut self.head, None);
        while let Some(mut boxed_node) = cur_link {
            cur_link = mem::replace(&mut boxed_node.next, None);
        }
    }
}

这稍微好一点,但是大的胜利将来自 Option 的方法。

首先,mem::replace(&mut Option,None) 是一个非常常见的习惯用法,以至于 Option 实际上直接采用了一种方法: take。

pub struct List {
    head: Link,
}

type Link = Option<Box<Node>>;

struct Node {
    elem: i32,
    next: Link,
}

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

    pub fn push(&mut self, elem: i32) {
        let new_node = Box::new(Node {
            elem: elem,
            next: self.head.take(),
        });

        self.head = Some(new_node);
    }

    pub fn pop(&mut self) -> Option<i32> {
        match self.head.take() {
            None => None,
            Some(node) => {
                self.head = node.next;
                Some(node.elem)
            }
        }
    }
}

impl Drop for List {
    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();
        }
    }
}

其次,match option { None => None, Some(x) => Some(y) } 是一种非常常见的习惯用法,因此被称为 map。map 在 Some(x) 中的 x 上执行一个函数来生成 Some(y) 中的 y。我们可以编写一个适当的函数并将其传递给map,但我们更愿意编写内联操作。

完成此操作的方法是使用闭包。 闭包是具有额外超级功能的匿名函数:它们可以引用闭包外部的局部变量!这使得它们在执行各种条件逻辑时非常有用。代码中唯一有 match 的地方是 pop 方法,所以让我们重写一下:

pub fn pop(&mut self) -> Option<i32> {
    self.head.take().map(|node| {
        self.head = node.next;
        node.elem
    })
}

啊,好多了。让我们确保我们没有破坏任何东西:

> cargo test

     Running target/debug/lists-5c71138492ad4b4a

running 2 tests
test first::test::basics ... ok
test second::test::basics ... ok

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

很好! 让我们继续改进代码的行为。

Last updated