Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Push

それでは、リストに値をプッシュする処理を書いてみましょう。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 が言っていることは正しいのですが、それが具体的に何を意味しているのか、 あるいはどう対処すべきなのかは、確かに明白ではありません。

借用された内容の外へ move することはできません

私たちは self.head フィールドを next に move しようとしていますが、Rust はそれを許してくれません。 そうすると、借用を終えて正当な所有者に「返す」ときに、self が部分的にしか初期化されていない状態になってしまいます。 前にも述べたように、それは &mut でできない唯一のことです。そんなことをするのは非常に失礼ですし、 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);
}
> 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 が実際に受け入れることもできそうなものですが、 受け入れてはくれません(さまざまな理由があります – 最も深刻なのは exception safety です)。 Rust にそれがなくなったと気づかれないように head を取り出す何らかの方法が必要です。助言を求めて、 悪名高い Rust ハッカー、インディアナ・ジョーンズに頼ってみましょう。

Indy Prepares to mem::replace

なるほど、インディーは 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);
}

ここでは、self.head をリストの新しい head で置き換える前に、一時的に Link::Empty で replace しています。 正直に言うと、これはやらなければならないこととしてはかなり残念なものです。 悲しいことに、私たちは(今のところ)そうしなければなりません。

とはいえ、これで push はすべて完了です! たぶん。正直なところ、たぶんテストすべきでしょう。 今のところ、それを行う最も簡単な方法はおそらく pop を書いて、正しい結果を生成することを確認することです。