テストケース:連結リスト
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`の状態によって振る舞いが // 変化するため、matchをする必要があります。 // `self`の型は`&List`なので、`*self`は`List`になります。マッチングは // 参照(`&T`)ではなく実体(`T`)に対して行うのが好ましいです。 // Rust 2018以降ではここでselfと(refのない)tailを使うことができ、 // &selfとref tailが推論されます。 // 参照 https://doc.rust-lang.org/edition-guide/rust-2018/ownership-and-lifetimes/default-match-bindings.html match *self { // `self`をすでに借用しているので、tailの所有権を取ることができません。 // 代わりに参照を使用します。 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()); }