Peek
好吧,我们通过了push和pop。我不想撒谎,这有点情绪化。编译时正确性是一种致命的毒药。
让我们通过做一些简单的事情来冷静一下:让我们实现 peek_front。 以前总是很容易的。 仍然很容易,对吗?
事实上,我认为可以复制粘贴它!
pub fn peek_front(&self) -> Option<&T> {
self.head.as_ref().map(|node| {
&node.elem
})
}等等,这次不行。
pub fn peek_front(&self) -> Option<&T> {
self.head.as_ref().map(|node| {
// BORROW!!!!
&node.borrow().elem
})
}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 一直是个麻烦。现在他们将成为一场噩梦。
为了理解这一点,我们需要回到借用的定义:
在布局部分我们说:
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 呢?
必须导入相关
嗯... 没错。我们有一个 Ref<Node<t>> ,但我们需要一个 Ref<T> 。我们可以放弃所有封装的希望,只是返回它。我们还可以使事情变得更加复杂,将 Ref<Node<T>> 包装成一个新类型,只暴露对 &T 的访问。
这两种选择都有点站不住脚。
相反,我们要深入下去。让我们找点乐子。我们的乐趣来源于这头野兽:
为借用数据的组件创建新的引用。
是的: 就像你可以映射一个 Option,你可以映射一个引用。
我相信某个地方的某个人一定很兴奋,因为 monads 或者其他什么东西,但是我不在乎这些。我也不认为这是一个合适的 monads,因为没有类似的情况,但我离题了。
这对我来说是最重要的,我需要这个。
让我们通过从堆栈中调出测试来确保这是可行的。 我们需要做些调整来处理 Ref 并未实现比较的事实。
太棒了!
Last updated
Was this helpful?