> 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-zao-gao-dan-an-quan-de-shuang-xiang-dui-lie/peek.md).

# 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

```

太棒了！
