テストケース:連結リスト

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());
}

参照

Box, メソッド