テストケース:連結リスト
enumの使用が適切なパターンのひとつに、連結リストを作成する場合があります。
use crate::List::*;
enum List {
// Cons:要素をラップし、次の要素へのポインタを保持するタプル構造体
Cons(u32, Box<List>),
// Nil:連結リストの終端であることを示すノード
Nil,
}
// 列挙型にはメソッドを付与することができます。
impl List {
// 空リストの作成
fn new() -> List {
// `Nil`は`List`型を持ちます。
Nil
}
// リストを受け取り、その始端に新しい要素を付加したものを返す関数
fn prepend(self, elem: u32) -> List {
// この`Cons`自体も、その第2要素もどちらもList型です。
Cons(elem, Box::new(self))
}
// リストの長さを返すメソッド
fn len(&self) -> u32 {
// `self` has to be matched, because the behavior of this method
// depends on the variant of `self`
// `self` has type `&List`, and `*self` has type `List`, matching on a
// concrete type `T` is preferred over a match on a reference `&T`
// after Rust 2018 you can use self here and tail (with no ref) below as well,
// rust will infer &s and ref tail.
// See https://doc.rust-lang.org/edition-guide/rust-2018/ownership-and-lifetimes/default-match-bindings.html
match *self {
// Can't take ownership of the tail, because `self` is borrowed;
// instead take a reference to the tail
// And it's a non-tail recursive call which may cause stack overflow for long lists.
Cons(_, ref tail) => 1 + tail.len(),
// 空リストならば長さは0
Nil => 0
}
}
// リストをヒープ上の文字列として表したものを返すメソッド
fn stringify(&self) -> String {
match *self {
Cons(head, ref tail) => {
// `format!`は`print!`に似ていますが、コンソール上に出力
// する代わりに、ヒープ上の文字列を返します。
format!("{}, {}", head, tail.stringify())
},
Nil => {
format!("Nil")
},
}
}
}
fn main() {
// 空の連結リストを作成。
let mut list = List::new();
// 要素を追加。
list = list.prepend(1);
list = list.prepend(2);
list = list.prepend(3);
// 追加後の状態を表示。
println!("linked list has length: {}", list.len());
println!("{}", list.stringify());
}