推入

push会使链表发生改变,所以我们需要使用&mut self。我们还需要取一个i32 来推入:

impl List {
    pub fn push(&mut self, elem: i32) {
        // TODO
    }
}

首先,我们需要做一个节点来存储我们的元素:

    pub fn push(&mut self, elem: i32) {
        let new_node = Node {
            elem: elem,
            next: ?????
        };
    }

next是什么?嗯,整个旧链表!我们能不能... ... 就这么做?

impl List {
    pub fn push(&mut self, elem: i32) {
        let new_node = Node {
            elem: elem,
            next: self.head,
        };
    }
}
> 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正告诉我们正确的事情,但它到底是什么意思,或者对它该怎么做,肯定不太显然:

不能移出借用的上下文

我们想把self.head字段移到next,但Rust不希望我们这么做。这将使self在我们结束 借用并将其“还给”它的合法所有者时,只被部分初始化。正如我们之前说过的,这是你不能用 &mut做的一件事:这将是超级无礼的,而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);
}
> 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没有注意到它消失的情况下得到头。为了寻求建议,我们求助于臭名昭著 的Rust黑客Indiana Jones:

Indy Prepares to mem::replace

啊,对了,Indy建议使用mem::replace手法。这个非常有用的函数可以让我们通过用另一个值替 换来从一个借用中偷取出来一个值。我们在文件的顶部拉入std::mem,让mem在本地作用域内:

use std::mem;

并适当地使用它:

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);
}

在这里,我们用Link::Empty暂时replace到self.head,然后再把self.head换成列表的新头。 我不会撒谎:这是一件非常不幸的事情。很遗憾,我们必须这么做(目前)。

但是,嘿,push全部完成! 可能吧。我们也许应该测试一下,说实话。现在,最简单的方法 可能是写pop,并确保它产生正确的结果。