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 は正しいことを言ってるんでしょうが,具体的に何を意味しているのか, どうすればいいのかわかりません:
cannot move out of borrowed content
借用中の値をムーブすることはできません
私たちは今 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 に気づかれないように,head
を取得する方法が必要なのです.
そこで,悪名高き Rust ハッカー,インディアナ・ジョーンズに助言を求めましょう:
おっ,インディは mem::replace
を使うことを提案していますね.
この信じられないほど便利な関数を使えば,値を別の値で 置き換える ことで,借りた値を盗むことができます.
mem
を使用するためには,ファイルの先頭に std::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
に replace
してから,リストの新しい head
に置き換えています.
率直に言って,これはかなり不満の残るコードです.しかし悲しいかな,そうせざるを得ないのですよ.
今のところはね.
でも,これで push
メソッドが完成しました!たぶんだけど.
正直,テストをしないとわかりません.
今テストをする最も簡単な方法は,おそらく pop
も書いて,正しい結果が得られるか確認することでしょう.