|
|
@ -87,3 +87,152 @@ impl<T> List<T> {
|
|
|
|
> cargo build
|
|
|
|
> cargo build
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
迄今为止一切运行正常,接下来的 `next` 实现起来会有些麻烦:
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
|
|
|
impl<'a, T> Iterator for Iter<'a, T> {
|
|
|
|
|
|
|
|
type Item = Ref<'a, T>;
|
|
|
|
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
|
|
|
|
|
|
self.0.take().map(|node_ref| {
|
|
|
|
|
|
|
|
self.0 = node_ref.next.as_ref().map(|head| head.borrow());
|
|
|
|
|
|
|
|
Ref::map(node_ref, |node| &node.elem)
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
|
|
|
cargo build
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
error[E0521]: borrowed data escapes outside of closure
|
|
|
|
|
|
|
|
--> src/fourth.rs:155:13
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
153 | fn next(&mut self) -> Option<Self::Item> {
|
|
|
|
|
|
|
|
| --------- `self` is declared here, outside of the closure body
|
|
|
|
|
|
|
|
154 | self.0.take().map(|node_ref| {
|
|
|
|
|
|
|
|
155 | self.0 = node_ref.next.as_ref().map(|head| head.borrow());
|
|
|
|
|
|
|
|
| ^^^^^^ -------- borrow is only valid in the closure body
|
|
|
|
|
|
|
|
| |
|
|
|
|
|
|
|
|
| reference to `node_ref` escapes the closure body here
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
error[E0505]: cannot move out of `node_ref` because it is borrowed
|
|
|
|
|
|
|
|
--> src/fourth.rs:156:22
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
153 | fn next(&mut self) -> Option<Self::Item> {
|
|
|
|
|
|
|
|
| --------- lifetime `'1` appears in the type of `self`
|
|
|
|
|
|
|
|
154 | self.0.take().map(|node_ref| {
|
|
|
|
|
|
|
|
155 | self.0 = node_ref.next.as_ref().map(|head| head.borrow());
|
|
|
|
|
|
|
|
| ------ -------- borrow of `node_ref` occurs here
|
|
|
|
|
|
|
|
| |
|
|
|
|
|
|
|
|
| assignment requires that `node_ref` is borrowed for `'1`
|
|
|
|
|
|
|
|
156 | Ref::map(node_ref, |node| &node.elem)
|
|
|
|
|
|
|
|
| ^^^^^^^^ move out of `node_ref` occurs here
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
果然,膝盖又中了一箭。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
`node_ref` 活得不够久,跟一般的引用不同,Rust 不允许我们这样分割 `Ref`,从 `head.borrow()` 中取出的 `Ref` 只允许跟 `node_ref` 活得一样久。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
而我们想要的函数是存在的:
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
|
|
|
pub fn map_split<U, V, F>(orig: Ref<'b, T>, f: F) -> (Ref<'b, U>, Ref<'b, V>) where
|
|
|
|
|
|
|
|
F: FnOnce(&T) -> (&U, &V),
|
|
|
|
|
|
|
|
U: ?Sized,
|
|
|
|
|
|
|
|
V: ?Sized,
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
喔,这个函数定义的泛型直接晃瞎了我的眼睛。。
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
|
|
|
|
|
|
self.0.take().map(|node_ref| {
|
|
|
|
|
|
|
|
let (next, elem) = Ref::map_split(node_ref, |node| {
|
|
|
|
|
|
|
|
(&node.next, &node.elem)
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.0 = next.as_ref().map(|head| head.borrow());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
elem
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
|
|
|
cargo build
|
|
|
|
|
|
|
|
Compiling lists v0.1.0 (/Users/ABeingessner/dev/temp/lists)
|
|
|
|
|
|
|
|
error[E0521]: borrowed data escapes outside of closure
|
|
|
|
|
|
|
|
--> src/fourth.rs:159:13
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
153 | fn next(&mut self) -> Option<Self::Item> {
|
|
|
|
|
|
|
|
| --------- `self` is declared here, outside of the closure body
|
|
|
|
|
|
|
|
...
|
|
|
|
|
|
|
|
159 | self.0 = next.as_ref().map(|head| head.borrow());
|
|
|
|
|
|
|
|
| ^^^^^^ ---- borrow is only valid in the closure body
|
|
|
|
|
|
|
|
| |
|
|
|
|
|
|
|
|
| reference to `next` escapes the closure body here
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
额,借用的内容只允许在闭包体中使用,看起来我们还是得用 `Ref::map` 来解决问题:
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
|
|
|
|
|
|
self.0.take().map(|node_ref| {
|
|
|
|
|
|
|
|
let (next, elem) = Ref::map_split(node_ref, |node| {
|
|
|
|
|
|
|
|
(&node.next, &node.elem)
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.0 = if next.is_some() {
|
|
|
|
|
|
|
|
Some(Ref::map(next, |next| &**next.as_ref().unwrap()))
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
None
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
elem
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
|
|
|
error[E0308]: mismatched types
|
|
|
|
|
|
|
|
--> src/fourth.rs:162:22
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
162 | Some(Ref::map(next, |next| &**next.as_ref().unwrap()))
|
|
|
|
|
|
|
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `fourth::Node`, found struct `std::cell::RefCell`
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
= note: expected type `std::cell::Ref<'_, fourth::Node<_>>`
|
|
|
|
|
|
|
|
found type `std::cell::Ref<'_, std::cell::RefCell<fourth::Node<_>>>`
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
晕, 多了一个 `RefCell` ,随着我们的对链表的逐步深入,`RefCell` 的代码嵌套变成了不可忽视的问题。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
看起来我们已经无能为力了,只能试着去摆脱 `RefCell` 了。`Rc` 怎么样?我们完全可以对 `Rc` 进行完整的克隆:
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
|
|
|
pub struct Iter<T>(Option<Rc<Node<T>>>);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl<T> List<T> {
|
|
|
|
|
|
|
|
pub fn iter(&self) -> Iter<T> {
|
|
|
|
|
|
|
|
Iter(self.head.as_ref().map(|head| head.clone()))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl<T> Iterator for Iter<T> {
|
|
|
|
|
|
|
|
type Item =
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
等等,那现在返回的是什么?`&T` 还是 `Ref<T>` ?
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
两者都不是,现在我们的 `Iter` 已经没有生命周期了:无论是 `&T` 还是 `Ref<T>` 都需要我们在 `next` 之前声明好生命周期。但是我们试图从 `Rc` 中取出来的值其实是迭代器的引用。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
也可以通过对 `Rc` 进行 map 获取到 `Rc<T>`?但是标准库并没有给我们提供相应的功能,第三方倒是有[一个](https://crates.io/crates/owning_ref)。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
但是,即使这么做了,还有一个更大的坑在等着:一个会造成迭代器不合法的可怕幽灵。事实上,之前我们对于迭代器不合法是免疫的,但是一旦迭代器产生 `Rc`,那它们就不再会借用链表。这意味着人们可以在持有指向链表内部的指针时,还可以进行 `push` 和 `pop` 操作。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
严格来说,`push` 问题不大,因为链表两端的增长不会对我们正在关注的某个子链表造成影响。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
但是 `pop` 就是另一个故事了,如果在我们关注的子链表之外 `pop`, 那问题不大。但是如果是 `pop` 一个正在引用的子链表中的节点呢?那一切就完了,特别是,如果大家还试图去 unwrap `try_unwrap` 返回的 `Result` ,会直接造成整个程序的 `panic`。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
仔细想一想,好像也不错,程序一切正常,除非去 `pop` 我们正在引用的节点,最美的是,就算遇到这种情况,程序也会直接崩溃,提示我们错误的发生。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
其实我们大部分的努力都是为了实现隐藏的细节和优雅的 API,典型的二八原则,八成时间花在二成的细节上。但是如果不关心这些细节,可以接受自己的平凡的话,那把节点简单的到处传递就行。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
总之,可以看出,内部可变性非常适合写一个安全性的应用程序,但是如果是安全性高的库,那内部可变性就有些捉襟见肘了。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
最终,我选择了放弃,不再实现 `Iter` 和 `IterMut`,也许努力下,可以实现,但是。。。不愉快,算了。
|