|
|
|
@ -129,4 +129,136 @@ pub fn push(&mut self, elem: i32) {
|
|
|
|
|
|
|
|
|
|
但是不管怎样,我们成功的完成了 `push` 方法,下面再来看看 `pop`。
|
|
|
|
|
|
|
|
|
|
## Pop
|
|
|
|
|
## Pop
|
|
|
|
|
`push` 是插入元素,那 `pop` 自然就是推出一个元素,因此也需要使用 `&mut self`,除此之外,推出的元素需要被返回,这样调用者就可以获取该元素:
|
|
|
|
|
```rust
|
|
|
|
|
pub fn pop(&mut self) -> Option<i32> {
|
|
|
|
|
// TODO
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
我们还需要一个办法来根据 `Link` 是否有值进行不同的处理,这个可以使用 `match` 来进行模式匹配:
|
|
|
|
|
```rust
|
|
|
|
|
pub fn pop(&mut self) -> Option<i32> {
|
|
|
|
|
match self.head {
|
|
|
|
|
Link::Empty => {
|
|
|
|
|
// TODO
|
|
|
|
|
}
|
|
|
|
|
Link::More(node) => {
|
|
|
|
|
// TODO
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
目前的代码显然会报错,因为函数的返回值是 `Option<T>` 枚举,而目前的返回值是 [`()`](https://course.rs/basic/base-type/function.html#无返回值)。当然,我们可以返回一个`Option<T>` 的枚举成员 `None`,但是一个更好的做法是使用 `unimplemented!()`,该宏可以明确地说明目前的代码还没有实现,一旦代码执行到 `unimplemented!()` 的位置,就会发生一个 `panic`。
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
pub fn pop(&mut self) -> Option<i32> {
|
|
|
|
|
match self.head {
|
|
|
|
|
Link::Empty => {
|
|
|
|
|
// TODO
|
|
|
|
|
}
|
|
|
|
|
Link::More(node) => {
|
|
|
|
|
// TODO
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
unimplemented!()
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
`panics` 是一种[发散函数](https://course.rs/basic/base-type/function.html?search=#永不返回的函数),该函数永不返回任何值,因此可以用于需要返回任何类型的地方。这句话很不好理解,但是从上面的代码中可以看出 `unimplemented!()` 是永不返回的函数,但是它却可以用于一个返回 `Option<i32>` 的函数中来替代返回值。
|
|
|
|
|
|
|
|
|
|
以上代码果不其然又报错了:
|
|
|
|
|
```shell
|
|
|
|
|
> cargo build
|
|
|
|
|
|
|
|
|
|
error[E0507]: cannot move out of borrowed content
|
|
|
|
|
--> src/first.rs:28:15
|
|
|
|
|
|
|
|
|
|
|
28 | match self.head {
|
|
|
|
|
| ^^^^^^^^^
|
|
|
|
|
| |
|
|
|
|
|
| cannot move out of borrowed content
|
|
|
|
|
| help: consider borrowing here: `&self.head`
|
|
|
|
|
...
|
|
|
|
|
32 | Link::More(node) => {
|
|
|
|
|
| ---- data moved here
|
|
|
|
|
|
|
|
|
|
|
note: move occurs because `node` has type `std::boxed::Box<first::Node>`, which does not implement the `Copy` trait
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
好在编译器偷偷提示了我们使用借用来替代所有权转移: `&self.head`。修改后,如下:
|
|
|
|
|
```rust
|
|
|
|
|
pub fn pop(&mut self) -> Option<i32> {
|
|
|
|
|
match &self.head {
|
|
|
|
|
Link::Empty => {
|
|
|
|
|
// TODO
|
|
|
|
|
}
|
|
|
|
|
Link::More(node) => {
|
|
|
|
|
// TODO
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
unimplemented!()
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
是时候填写相应的逻辑了:
|
|
|
|
|
```rust
|
|
|
|
|
pub fn pop(&mut self) -> Option<i32> {
|
|
|
|
|
let result;
|
|
|
|
|
match &self.head {
|
|
|
|
|
Link::Empty => {
|
|
|
|
|
result = None;
|
|
|
|
|
}
|
|
|
|
|
Link::More(node) => {
|
|
|
|
|
result = Some(node.elem);
|
|
|
|
|
self.head = node.next;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
result
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
当链表为 `Empty` 时,返回一个 `None`,表示我们没有 `pop` 到任何元素;若不为空,则返回第一个元素,并将 `head` 指向下一个节点 `node.next`。但是这段代码又报错了:
|
|
|
|
|
```shell
|
|
|
|
|
error[E0507]: cannot move out of `node.next` which is behind a shared reference
|
|
|
|
|
--> src/first.rs:37:29
|
|
|
|
|
|
|
|
|
|
|
37 | self.head = node.next;
|
|
|
|
|
| ^^^^^^^^^ move occurs because `node.next` has type `Link`, which does not implement the `Copy` trait
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
原因是试图转移 `node` 的所有权,但只有它的引用。回头仔细看看代码,会发现这里的关键是我们希望移除一些东西,这意味着需要通过值的方式获取链表的 head。看来只能故技重施了:
|
|
|
|
|
```rust
|
|
|
|
|
pub fn pop(&mut self) -> Option<i32> {
|
|
|
|
|
let result;
|
|
|
|
|
match std::mem::replace(&mut self.head, Link::Empty) {
|
|
|
|
|
Link::Empty => {
|
|
|
|
|
result = None;
|
|
|
|
|
}
|
|
|
|
|
Link::More(node) => {
|
|
|
|
|
result = Some(node.elem);
|
|
|
|
|
self.head = node.next;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
result
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
我们将 `self.head` 的值偷出来,然后再将 `Link::Empty` 填回到 `self.head` 中。此时用于 `match` 匹配的就是一个拥有所有权的值类型,而不是之前的引用类型。
|
|
|
|
|
|
|
|
|
|
事实上,上面的代码有些啰嗦,我们可以直接在 `match` 的两个分支中通过表达式进行返回:
|
|
|
|
|
```rust
|
|
|
|
|
pub fn pop(&mut self) -> Option<i32> {
|
|
|
|
|
match std::mem::replace(&mut self.head, Link::Empty) {
|
|
|
|
|
Link::Empty => None,
|
|
|
|
|
Link::More(node) => {
|
|
|
|
|
self.head = node.next;
|
|
|
|
|
Some(node.elem)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
这样修改后,代码就更加简洁,可读性也更好了,至此链表的基本操作已经完成,下面让我们写一个测试代码来测试下它的功能和正确性。
|