Pop

push一样,pop想要改变列表;除此之外,我们还想返回结果。然而pop还得处理一个特殊的边界情况:如果列表是空的呢?为了表示这个情况,我们使用可靠的Option类型:+

pub fn pop(&mut self) -> Option<i32> {
    //TODO
}

Option<T>是一个表示一个值可能存在也可能不存在的enum。它要么是Some(T),要么是None。我们也可以像Link一样创建一个自己的enum,但是我们想让用户了解我们的返回类型到底是什么,而Option是如此的无处不在,每个人都知道它。实际上,因为它是如此的基本,它被隐式的导入到了每一个源文件的作用域中,也包括它的两个变体:SomeNone(这样我们就不用写Option::None)。

Option<T>尖括号里的部分指出Option实际上是一个泛型,它的泛型参数是T。这意味着你可以创建一个任何类型的Option!

所以,我们有这个Link了,我们怎么检查它是Empty还是More呢?使用match进行模式匹配:

pub fn pop(&mut self) -> Option<i32> {
    match self.head {
        Link::Empty => {
            // TODO
        }
        Link::More(node) => {
            // TODO
        }
    };
}
> cargo build

error[E0308]: mismatched types
  --> src/first.rs:27:30
   |
27 |     pub fn pop(&mut self) -> Option<i32> {
   |            ---               ^^^^^^^^^^^ expected enum `std::option::Option`, found ()
   |            |
   |            this function's body doesn't return
   |
   = note: expected type `std::option::Option<i32>`
              found type `()`

啊,pop必须返回一个值,我们还没做这件事。我们可以直接返回None,但是在这情况下,返回unimplemented!来指出我们没有完成该函数的实现会更好。unimplemented!是一个宏(!代表一个宏),它会在被调用的时候让整个程序panic(基本上也就是以可控的方式崩溃)。

无条件panic是一个发散函数(diverging function)的例子。发散函数永远不会返回到调用者,所以无论一个地方期待何种类型的返回值,它的返回值都能拿来用。在这里,unimplemented!被使用在期待一个Option<T>的地方。

注意到我们不需要在程序里写return。函数中的最后一个表达式也就隐式的成为它的返回值。这让我们可以更精炼的表达简单的逻辑。你也可以像C系语言一样,显式的return返回。

噢,Rust,别纠缠不休了!和往常一样,Rust对我们非常生气。值得感谢的是,这次它还给出了深入的信息! 默认情况下,模式匹配将尝试将其内容移动到新分支中,但是我们不能这样做,因为我们在这里不拥有自己的值。

Rust 表示我们应该在 match 添加一个引用来解决这个问题。🤷‍️让我们试试:

好耶,又编译了!现在让我们搞清楚实现逻辑。我们要创建一个Option,所以要为这个预留一个变量。在Empty情况下要返回 None,在More 情况下需要返回Some(i32),并且改变列表的head。来吧:

我们试图移出节点,但我们只有一个对它的共享引用。

我们也许应该退一步想想我们要做什么。我们希望:

  • 检查列表是否为空。

  • 如果是空的,返回None

  • 如果是非空

    • 移除list头部

    • 移除该头部的elem

    • 将列表的head替换为next

    • 返回Some(elem)

重要的一点事我们想要删除东西,这意味着我们需要按值获取list的head。我们肯定不能通过由ref node获取的共享引用来做这件事。我们也“只”拥有一个可变引用,所以能移动东西的唯一方法就是替换它。看来我们又在做Empty替换那一套了!

让我们试试这个:

我 的 天 哪

它编译了,一个警告都没有!!!!!

这里我要给出我的优化提示了:我们现在返回的是result变量的值,但实际上根本不用这么做!就像一个函数的结果是它的最后一个表达式,每个代码块的结果也是它的最后一个表达式。通常我们使用分号来阻止这一行为,这会让代码块的值变成空元组(tuple)()。这实际上也是不声明返回值的函数——例如push——返回的。

所以,我们可以把 pop 写成:

这是一个更简洁和习惯用法。请注意,Link::Empty分支完全丢失了它的大括号,因为我们只有一个表达式要计算。这是对于简单情况的简便处理。

不错,仍然能工作!

Last updated

Was this helpful?