1.4 入栈
原文链接: https://rust-unofficial.github.io/too-many-lists/first-push.html
我们来写把值压入链表的操作。push 会修改链表,所以需要 &mut self。还要接收一个要压入的 i32:
1
2
3
4
5
| impl List {
pub fn push(&mut self, elem: i32) {
// TODO
}
}
|
首先,需要创建一个节点来存放元素:
1
2
3
4
5
6
| pub fn push(&mut self, elem: i32) {
let new_node = Node {
elem: elem,
next: ?????
};
}
|
next 放什么?嗯,整个旧链表!我们能……就这么做吗?
1
2
3
4
5
6
7
8
| impl List {
pub fn push(&mut self, elem: i32) {
let new_node = Node {
elem: elem,
next: self.head,
};
}
}
|
1
2
3
4
5
6
| > cargo build
error[E0507]: cannot move out of borrowed content
--> src/first.rs:19:19
|
19 | next: self.head,
| ^^^^^^^^^ cannot move out of borrowed content
|
不行。Rust 说得对,但到底什么意思、该怎么办,确实不那么明显:
cannot move out of borrowed content
我们想把 self.head 字段移给 next,但 Rust 不让我们这么做。这样在借用结束、把 self「还回去」给合法主人时,self 就只被部分初始化了。正如前面说的,这是 &mut 唯一不能做的事:那太不礼貌了,Rust 很有礼貌(当然这也极其危险,但肯定不是它在乎的原因)。
要是放点东西回去呢?也就是我们正在创建的那个节点:
1
2
3
4
5
6
7
8
| pub fn push(&mut self, elem: i32) {
let new_node = Box::new(Node {
elem: elem,
next: self.head,
});
self.head = Link::More(new_node);
}
|
1
2
3
4
5
6
| > cargo build
error[E0507]: cannot move out of borrowed content
--> src/first.rs:19:19
|
19 | next: self.head,
| ^^^^^^^^^ cannot move out of borrowed content
|
没戏。原则上 Rust 其实可以接受这种写法,但它不会(原因很多——最严重的是异常安全)。我们需要某种方式在 Rust 察觉不到的情况下拿走 head。求教时,我们转向臭名昭著的 Rust 黑客印第安纳·琼斯:

啊对,Indy 建议 mem::replace 这一招。这个极其有用的函数让你通过替换为另一个值,从借用中偷走一个值。在文件顶部引入 std::mem,让 mem 在局部作用域里可用:
然后恰当使用:
1
2
3
4
5
6
7
8
| pub fn push(&mut self, elem: i32) {
let new_node = Box::new(Node {
elem: elem,
next: mem::replace(&mut self.head, Link::Empty),
});
self.head = Link::More(new_node);
}
|
这里我们先临时把 self.head 用 mem::replace 换成 Link::Empty,再换成链表的新 head。不瞒你说:不得不这么做挺遗憾的。可惜,目前只能这样(暂时)。
不过嘿,push 算写完了!大概吧。老实说应该测一下。现在最简单的办法大概是写 pop,并确认结果正确。