> For the complete documentation index, see [llms.txt](https://chapin666.gitbook.io/too-many-list-zh/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://chapin666.gitbook.io/too-many-list-zh/yi-ge-hai-hang-de-dan-lian-zhan/peek.md).

# Peek

有一件事我们上次甚至没有去做，那就是 peek。我们开始吧，我们需要做的就是返回对列表头元素的引用（如果它存在的话）。听起来很简单，让我们试试：

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

```rust
> 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，这会将Option从它所在的对象中移出。以前这是好的，因为我们刚刚把它拿出来，但现在我们真的想把它留在原来的地方。正确的处理方法是使用 Option 上的 as\_ref 方法，它有以下定义：

```rust
impl<T> Option<T> {
    pub fn as_ref(&self) -> Option<&T>;
}
```

它将 Option 降级为对其内部的引用。我们可以自己用一个显式的匹配来完成，但是不可以。这确实意味着我们需要做一个额外的解引用来切断额外的间接寻址，但谢天谢地，operator 为我们处理。

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

```rust
cargo build

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

搞定了。

我们也可以使用 as\_mut 创建一个可变的版本：

```rust
pub fn peek_mut(&mut self) -> Option<&mut T> {
    self.head.as_mut().map(|node| {
        &mut node.elem
    })
}
```

```rust
lists::cargo build
```

别忘了测试一下：

```rust
#[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));
}
```

```rust
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
```

这很好，但是我们并没有真正的测试看看我们是否可以改变peek\_mut的返回值，是吗？如果一个引用是可变的，但是没有人对它进行变异，那么我们真的测试过它的可变性吗？让我们试着在这个选项上使用map，在以下位置输入一个深刻的值：

```rust
#[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));
}
```

```rust
> 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          ^~~~~
```

编译器抱怨值是不可变的，但是我们很清楚地写下了\&mut value；给出了什么？事实证明，以这种方式编写闭包的参数并没有指定值是可变引用。相反，它会创建一个模式，该模式将与闭包的参数相匹配；|\&mut value |意味着“参数是一个可变的引用，但是请将它指向的值复制到value中”。如果我们只使用 |value |，那么值的类型将是\&mut i32，我们实际上可以改变head：

```rust
#[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));
}
```

```rust
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
```

好多了！
