From 8e27a57bed35eb4dc32bd6754dddae020059a8c2 Mon Sep 17 00:00:00 2001 From: sunface Date: Thu, 10 Mar 2022 21:17:54 +0800 Subject: [PATCH] add links/basic-operations --- src/SUMMARY.md | 4 +- src/basic/method.md | 1 + .../bad-stack/basic-operations.md | 132 ++++++++++++++++++ src/too-many-lists/bad-stack/new.md | 1 - 4 files changed, 135 insertions(+), 3 deletions(-) create mode 100644 src/too-many-lists/bad-stack/basic-operations.md delete mode 100644 src/too-many-lists/bad-stack/new.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 14e68336..8e516d21 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -157,8 +157,8 @@ - [我们到底需不需要链表](too-many-lists/do-we-need-it.md) - [不太优秀的单向链表:栈](too-many-lists/bad-stack/intro.md) - [数据布局](too-many-lists/bad-stack/layout.md) - src/first.rs:23:19 + | +23 | next: self.head, + | ^^^^^^^^^ move occurs because `self.head` has type `Link`, which does not implement the `Copy` trait +``` + + +但是,如上所示,这段代码会报错,因为试图将借用的值 `self` 中的 `head` 字段的所有权转移给 `next` ,在 Rust 中这是不被允许的。那如果我们试图将值再放回去呢? +```rust +pub fn push(&mut self, elem: i32) { + let new_node = Box::new(Node { + elem: elem, + next: self.head, + }); + + self.head = Link::More(new_node); +} +``` + +其实在写之前,应该就预料到结果了,显然这也是不行的,虽然从我们的角度来看还挺正常的,但是 Rust 并不会接受(有多种原因,其中主要的是[Exception safety](https://doc.rust-lang.org/nightly/nomicon/exception-safety.html))。 + +我们需要一个办法,让 Rust 不再阻挠我们,其中一个可行的办法是使用 `clone`: +```rust +pub struct List { + head: Link, +} + +#[derive(Clone)] +enum Link { + Empty, + More(Box), +} + +#[derive(Clone)] +struct Node { + elem: i32, + next: Link, +} + +impl List { + pub fn new() -> Self { + List { head: Link::Empty } + } + + pub fn push(&mut self, elem: i32) { + let new_node = Node { + elem: elem, + next: self.head.clone(), + }; + } +} +``` + +`clone` 用起来简单难,且可解万愁,但是。。。既然是链表,性能那自然是很重要的,特别是要封装成库给其他代码使用时,那性能更是重中之重。 + +没办法了,我们只能向大名鼎鼎的 Rust 黑客 Indiana Jones求助了: + + +经过一番诚心祈愿,Indy 建议我们使用 `mem::replace` 秘技。这个非常有用的函数允许我们从一个借用中偷出一个值的同时再放入一个新值。 +```rust +pub fn push(&mut self, elem: i32) { + let new_node = Box::new(Node { + elem: elem, + next: std::mem::replace(&mut self.head, Link::Empty), + }); + + self.head = Link::More(new_node); +} +``` + +这里,我们从借用 `self` 中偷出了它的值 `head` 并赋予给 `next` 字段,同时将一个新值 `Link::Empty` 放入到 `head` 中,成功完成偷梁换柱。不得不说,这个做法非常刺激,但是很不幸的是,目前为止,最好的办法可能也只能是它了。 + +但是不管怎样,我们成功的完成了 `push` 方法,下面再来看看 `pop`。 + +## Pop \ No newline at end of file diff --git a/src/too-many-lists/bad-stack/new.md b/src/too-many-lists/bad-stack/new.md deleted file mode 100644 index e65f941e..00000000 --- a/src/too-many-lists/bad-stack/new.md +++ /dev/null @@ -1 +0,0 @@ -# New