Peek 函数

在之前章节中,我们定义了 pushpop 等基础操作,下面一起添加几个进阶操作,让我们的链表有用起来。

首先实现的就是 peek 函数,它会返回链表的表头元素的引用:

#![allow(unused)]
fn main() {
pub fn peek(&self) -> Option<&T> {
    self.head.map(|node| {
        &node.elem
    })
}
}
$ cargo build

error[E0515]: cannot return reference to local data `node.elem`
  --> src/second.rs:37:13
   |
37 |             &node.elem
   |             ^^^^^^^^^^ returns a reference to data owned by the current function

error[E0507]: cannot move out of borrowed content
  --> src/second.rs:36:9
   |
36 |         self.head.map(|node| {
   |         ^^^^^^^^^ cannot move out of borrowed content

哎,Rust 大爷,您又哪里不满意了。不过问题倒是也很明显: map 方法是通过 self 获取的值,我们相当于把内部值的引用返回给函数外面的调用者。

一个比较好的解决办法就是让 map 作用在引用上,而不是直接作用在 self.head 上,为此我们可以使用 Optionas_ref 方法:

#![allow(unused)]
fn main() {
impl<T> Option<T> {
    pub fn as_ref(&self) -> Option<&T>;
}
}

该方法将一个 Option<T> 变成了 Option<&T>,然后再调用 map 就会对引用进行处理了:

#![allow(unused)]
fn main() {
pub fn peek(&self) -> Option<&T> {
    self.head.as_ref().map(|node| {
        &node.elem
    })
}
}
$ cargo build

    Finished dev [unoptimized + debuginfo] target(s) in 0.32s

当然,我们还可以通过类似的方式获取一个可变引用:

#![allow(unused)]
fn main() {
pub fn peek_mut(&mut self) -> Option<&mut T> {
    self.head.as_mut().map(|node| {
        &mut node.elem
    })
}
}

至此 peek 已经完成,为了测试它的功能,我们还需要编写一个测试用例:

#![allow(unused)]
fn main() {
#[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_mut().map(|&mut value| {
        value = 42
    });

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

error[E0384]: cannot assign twice to immutable variable `value`
   --> src/second.rs:100:13
    |
99  |         list.peek_mut().map(|&mut value| {
    |                                   -----
    |                                   |
    |                                   first assignment to `value`
    |                                   help: make this binding mutable: `mut value`
100 |             value = 42
    |             ^^^^^^^^^^ cannot assign twice to immutable variable          ^~~~~

天呐,错误源源不断,这次编译器抱怨说 value 是不可变的,但是我们明明使用 &mut value,发生了什么?难道说在闭包中通过这种方式申明的可变性实际上没有效果?

实际上 &mut value 是一个模式匹配,它用 &mut value 模式去匹配一个可变的引用,此时匹配出来的 value 显然是一个值,而不是可变引用,因为只有完整的形式才是可变引用!

因此我们没必要画蛇添足,这里直接使用 |value| 来匹配可变引用即可,那么此时匹配出来的 value 就是一个可变引用。

#![allow(unused)]
fn main() {
#[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_mut().map(|value| {
        *value = 42
    });

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

这次我们直接匹配出来可变引用 value,然后对其修改即可。

$ cargo test

     Running target/debug/lists-5c71138492ad4b4a

running 3 tests
test first::test::basics ... ok
test second::test::basics ... ok
test second::test::peek ... ok

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