Push

ではリストに値を追加(push)する操作を書いていきましょう.pushは リストの変更を伴うので&mut selfを引数に取ります.pushするi32の値も 引数に取る必要があります:

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

まずは要素をいれるNodeを作りましょう.

    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は私達に正論を突きつけています.でもこれが具体的に何を 意味するのか,あるいはどうすればいいかは判然としません:

cannot move out of borrowed content

私達はself.headnextにムーブしようとしましたが,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的にはOKなのですが,実際にはだめです(理由はいくつか ありますが,例外に対する安全性が一番深刻な問題です).Rustに値を取ったと 気づかれないように取る方法が必要です.ここは悪名高いRustのハッカー, インディ・ジョーンズに教えを請うことにしましょう:

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をLink::Emptyにしています. 今から嘘をつきます.こういうことをしなきゃいけないのは不幸なことです.こうする 他に方法はないのです(いまのところは).

でもこれでpushは完成です!多分.多分テストしたほうが良さそうですね,正直. とりあえず一番簡単なやり方はpopを書いて,予期したとおりに動くことを確認する ことでしょう.