# Peek

好吧，我们通过了push和pop。我不想撒谎，这有点情绪化。编译时正确性是一种致命的毒药。

让我们通过做一些简单的事情来冷静一下：让我们实现 peek\_front。 以前总是很容易的。 仍然很容易，对吗？

事实上，我认为可以复制粘贴它！

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

等等，这次不行。

```rust
pub fn peek_front(&self) -> Option<&T> {
    self.head.as_ref().map(|node| {
        // BORROW!!!!
        &node.borrow().elem
    })
}
```

```rust
cargo build

error[E0515]: cannot return value referencing temporary value
  --> src/fourth.rs:66:13
   |
66 |             &node.borrow().elem
   |             ^   ----------^^^^^
   |             |   |
   |             |   temporary value created here
   |             |
   |             returns a value referencing data owned by the current function
```

好吧，我正在烧我的电脑。

这与我们的单链堆栈完全相同。 为什么情况有所不同。

这个问题的答案实际上就是这一章的全部寓意: RefCell 让一切都变得悲伤。到目前为止，RefCell 一直是个麻烦。现在他们将成为一场噩梦。

为了理解这一点，我们需要回到借用的定义：

```rust
fn borrow<'a>(&'a self) -> Ref<'a, T>
fn borrow_mut<'a>(&'a self) -> RefMut<'a, T>
```

在布局部分我们说：

> RefCell 不是在静态地强制执行此操作，而是在运行时强制执行它们。 如果您违反了规则，RefCell将会慌乱并使程序崩溃。 为什么返回这些Ref 和RefMut 东西？ 它们的行为本质上类似于Rcs，只是为了借用。 同样，它们会保留RefCell借用，直到超出范围。 我们稍后再讨论。

这是以后的事了。

Ref 和 RefMut 分别实现了 Deref 和 DerefMut。因此，对于大多数意图和目的来说，它们的行为完全像 \&t 和 \&mut t。然而，由于这些特性的工作原理，返回的引用被连接到 Ref 的生存期，而不是实际的 RefCell。这意味着只要我们一直保持引用不变，就必须坐在Ref周围。

实际上，这对于正确性是必需的。 当Ref被丢弃时，它告诉RefCell它不再借用。 因此，如果我们确实设法将引用保留的时间超过了Ref的存在时间，那么当引用开始运行时，我们可以获得RefMut，并将Rust的类型系统完全破坏了一半。

那么我们该怎么办呢？我们只想返回一个引用，但我们需要保持这个 Ref 的东西。但是，只要我们从 peek 返回引用，函数就结束了，Ref 也超出了作用域。

😖

据我所知，我们实际上已经完全陷入困境了。你不能像完全封装 RefCell 那样。

但是... 如果我们放弃完全隐藏我们的实现细节呢? 如果我们返回 Refs 呢？

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

```rust
> cargo build

error[E0412]: cannot find type `Ref` in this scope
  --> src/fourth.rs:63:40
   |
63 |     pub fn peek_front(&self) -> Option<Ref<T>> {
   |                                        ^^^ not found in this scope
help: possible candidates are found in other modules, you can import them into scope
   |
1  | use core::cell::Ref;
   |
1  | use std::cell::Ref;
   |
```

必须导入相关

```rust
use std::cell::{Ref, RefCell};
```

```rust
> cargo build

error[E0308]: mismatched types
  --> src/fourth.rs:64:9
   |
64 | /         self.head.as_ref().map(|node| {
65 | |             node.borrow()
66 | |         })
   | |__________^ expected type parameter, found struct `fourth::Node`
   |
   = note: expected type `std::option::Option<std::cell::Ref<'_, T>>`
              found type `std::option::Option<std::cell::Ref<'_, fourth::Node<T>>>`

```

嗯... 没错。我们有一个  Ref\<Node\<t>> ，但我们需要一个 Ref\<T> 。我们可以放弃所有封装的希望，只是返回它。我们还可以使事情变得更加复杂，将 Ref\<Node\<T>> 包装成一个新类型，只暴露对 \&T 的访问。

这两种选择都有点站不住脚。

相反，我们要深入下去。让我们找点乐子。我们的乐趣来源于这头野兽：

```rust
map<U, F>(orig: Ref<'b, T>, f: F) -> Ref<'b, U>
    where F: FnOnce(&T) -> &U,
          U: ?Sized
```

> 为借用数据的组件创建新的引用。

是的: 就像你可以映射一个 Option，你可以映射一个引用。

我相信某个地方的某个人一定很兴奋，因为 *monads* 或者其他什么东西，但是我不在乎这些。我也不认为这是一个合适的  *monads*，因为没有类似的情况，但我离题了。

这对我来说是最重要的，我需要这个。

```rust
pub fn peek_front(&self) -> Option<Ref<T>> {
    self.head.as_ref().map(|node| {
        Ref::map(node.borrow(), |node| &node.elem)
    })
}
```

```rust
> cargo build
```

让我们通过从堆栈中调出测试来确保这是可行的。 我们需要做些调整来处理 Ref 并未实现比较的事实。

```rust
#[test]
fn peek() {
    let mut list = List::new();
    assert!(list.peek_front().is_none());
    list.push_front(1); list.push_front(2); list.push_front(3);

    assert_eq!(&*list.peek_front().unwrap(), &3);
}
```

```rust
> cargo test

     Running target/debug/lists-5c71138492ad4b4a

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

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

```

太棒了！


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://chapin666.gitbook.io/too-many-list-zh/yi-ge-zao-gao-dan-an-quan-de-shuang-xiang-dui-lie/peek.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
