[Rust]列挙型とパターンマッチングの活用

## `enum`を使った列挙型

`enum`を使うと、一つの名前付き列挙型に、複数の型情報を含めた上で、そのうちの一つを選ばせることができます。
列挙型の一つ一つの型情報は互いに異なるものとして扱われます。

Rustの列挙型の特徴として、各要素の型を柔軟に決められる、という点があります。すなわち、ある列挙型のうちある要素は具体的な`String`の値を持ち、また別の要素は具体的な`(i32, i32)`の値を持つ、といったことが可能です（もちろん、何も値を持たせずに要素名だけ定義することも可能です！）。

```rust
enum Message {
    Text(String),
    Move { x: i32, y: i32 },
    Quit,
}
```
そしてそれらは一つの列挙型という同じ型として扱えるので、例えば関数の引数の型として用いることができます（下記のコードは後述するパターンマッチングを使用しています）。

```rust
fn print_message(msg: Message) {
    match msg {
        Message::Text(content) => println!("Text: {}", content),
        Message::Move { x, y } => println!("Move to: ({}, {})", x, y),
        Message::Quit => println!("Quit"),
    }
}
```

列挙型を実際に参照する場合は、`列挙型::要素名`という記法を用います

```rust
fn main() {
    let msg1 = Message::Text(String::from("Hello"));
    let msg2 = Message::Move { x: 10, y: 20 };
    let msg3 = Message::Quit;

    print_message(msg1);
    print_message(msg2);
    print_message(msg3);
}
```

:::note
この機構は非常に興味深いです。TypeScriptには標準のEnumがありますが、正直なところ利用シーンは限られ、実際はオブジェクトリテラルで代用することが多いでしょう。それに比べると、言語の仕様としてこのような柔軟な機構を持つのは（実際に使うかはともかく）特徴的です。
:::

## `Option`という列挙型

`Option`はRustが標準で提供する列挙型です。その実装は以下のようになっています。

```rust
enum Option<T> {
    Some(T),
    None,
}
```

任意の型`T`について、値が存在していれば`Some`という要素にその値が格納されています。もし存在していなければ`Option<T>`は`None`になります。
`Option`を使うメリットは、安全にNullableなデータを表現できることにあります。`T`と`Option<T>`は明確に違う型なので、`Option`型の変数を、あたかも値が必ず存在しているように扱うことはできません（コンパイルエラーになります）。この機構により、値の存在有無を確認するコードが強制され、エラーを防ぐことができます。

```rust
fn divide(a: i32, b: i32) -> Option<i32> {
    if b == 0 {
        None
    } else {
        Some(a / b)
    }
}

fn main() {
    match divide(10, 2) {
        Some(result) => println!("Result: {}", result),
        None => println!("Cannot divide by zero"),
    }
}
```

## 列挙型とパターンマッチング

列挙型と`match`を組み合わせることで、強力なパターンマッチングが可能になります。
`match`は列挙型にも対応しており、要素に応じて処理を分けることができます。
列挙型の要素が値を持っている場合、`要素名(変数名)`のようにしてその値を参照することもできます。

```rust
enum Shape {
    Circle(f64),
    Rectangle { width: f64, height: f64 },
    Triangle(f64, f64, f64),
}

fn describe_shape(shape: Shape) {
    match shape {
        Shape::Circle(radius) => println!("Circle with radius {}", radius),
        Shape::Rectangle { width, height } => {
            println!("Rectangle with width {} and height {}", width, height)
        }
        Shape::Triangle(a, b, c) => println!("Triangle with sides {}, {}, {}", a, b, c),
    }
}

fn main() {
    let shape = Shape::Rectangle { width: 10.0, height: 5.0 };
    describe_shape(shape);
}
```

`Option`と組み合わせると、「値が存在するとき」と「そうでないとき」のフローを分けることができます。値が存在するときは`Some`に値が保持されているので、`Some(変数名)`とすることでその値を参照できます。

```rust
fn print_number(opt: Option<i32>) {
    match opt {
        Some(value) => println!("Number: {}", value),
        None => println!("No number provided"),
    }
}

fn main() {
    print_number(Some(42));
    print_number(None);
}
```

さらに、「値が存在し、かつそれが特定の値のとき」のみ処理を実行したい場合は、`if let`構文が使えます。条件にマッチしたときのみ、その値を変数にバインディングしたうえで処理を実行できるため、`match`を使うよりも簡潔に記述できます。

```rust
fn main() {
    let opt = Some(42);

    if let Some(42) = opt {
        println!("The answer to life, the universe, and everything!");
    }
}
```

:::note
`Option`は強力な型安全システムのように感じます。TypeScriptでも型レベルで`undefined`や`null`を含めることができますが、Rustが持つパターンマッチングとの相性の良さはすごいと感じます。
:::