列挙型とパターンマッチング　その1

この記事は以下の記事を翻訳して、自分なりに理解した内容をまとめています。

https://doc.rust-lang.org/book/ch06-01-defining-an-enum.html

# Defining an Enum

例えば、IPアドレスはV4とV6同時に使用できないため、このようなものを定義するのにEnum列挙型を使用するのが適切。

```rust
enum IpAddrKind {
    V4,
    V6,
}
```

IpAddrKindデータ型が使用できるようになる。

## Enum Values

以下の様にIpAddrKindのインスタンスを作成できる。

```rust
    let four = IpAddrKind::V4;
    let six = IpAddrKind::V6;
```

例えば、任意のIpAddrKindを受け取る関数を定義した場合、

```rust
fn route(ip_kind: IpAddrKind) {}
```

この関数はどちらのバリアントでも呼び出すことができる。

```rust
    route(IpAddrKind::V4);
    route(IpAddrKind::V6);
```

実際のIPアドレスデータを保持する場合、それがどの種類かを知るために、構造体を使って定義することができる。

```rust
    enum IpAddrKind {
        V4,
        V6,
    }

    struct IpAddr {
        kind: IpAddrKind,
        address: String,
    }

    let home = IpAddr {
        kind: IpAddrKind::V4,
        address: String::from("127.0.0.1"),
    };

    let loopback = IpAddr {
        kind: IpAddrKind::V6,
        address: String::from("::1"),
    };
```

上の例では、homeにV4で127.0.0.1を関連付け、loopbackにV6で::1を関連付けた。

列挙型に直接データを配置することで、より簡潔に表現することができる。

```rust
    enum IpAddr {
        V4(String),
        V6(String),
    }

    let home = IpAddr::V4(String::from("127.0.0.1"));

    let loopback = IpAddr::V6(String::from("::1"));
```

この例では列挙型に直接値を設定したため、構造体の定義は不要になる。

さらにメリットとして、異なるタイプのデータを持つことができる。
例えばV4は0～255の値を持つ4つのデータであるのに対して、V6は一つの文字列として表現する。

```rust
    enum IpAddr {
        V4(u8, u8, u8, u8),
        V6(String),
    }

    let home = IpAddr::V4(127, 0, 0, 1);

    let loopback = IpAddr::V6(String::from("::1"));
```

蒸気のように様々な定義を紹介したが、結局の所、アプリによって使い方は様々なので、標準で使用できるライブラリがある。
標準ライブラリでどのように定義しているかというと、列挙型毎にことなる構造体を使用するようになっている。

```rust
struct Ipv4Addr {
    // --snip--
}

struct Ipv6Addr {
    // --snip--
}

enum IpAddr {
    V4(Ipv4Addr),
    V6(Ipv6Addr),
}
```

これは、数字、文字列、構造体など、あらゆる形で定義できることを示している。

以下を例に取ると、列挙型にさまざまなタイプが定義されている。

```rust
enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}
```

この例では、

* Quitには関連するデータは無い
* Moveには匿名の構造体が含まれている
* Writeには文字列が含まれている
* ChangeColorには3つのi32型が含まれている

これらが全てMessage列挙型に含まれている、ということ。
これは以下と同じ。

```rust
struct QuitMessage; // unit struct
struct MoveMessage {
    x: i32,
    y: i32,
}
struct WriteMessage(String); // tuple struct
struct ChangeColorMessage(i32, i32, i32); // tuple struct
```

しかし、これではMessage列挙型のように簡単に定義することはできない。

もう一つ、列挙型には構造体と同じように関数を定義することができる。

```rust
    impl Message {
        fn call(&self) {
            // method body would be defined here
        }
    }

    let m = Message::Write(String::from("hello"));
    m.call();
```

メソッドの本体はselfを使用して呼び出した値を取得している。
この例では、Message::Write(String::from("hello"))という値を持つmを作成し、m.call()を実行した。

## The Option Enum and Its Advantages Over Null Values

ここではOptionタイプについて説明する。
Optionタイプは値が何かである場合もあれば、何もない場合もある非常に一般的なケースを扱えるため、様々な場所で使用されている。
コンパイラは処理の必要があるところを全て処理したかを確認できるため、バグを防ぐことができる。

Rustにはnull機能は存在しない。
null値をnull以外の値として扱うとエラーになるため、この種のエラーは簡単に起こすことができる。
しかし、nullが表迂言しようとしている概念は有用。
現在無効であるか、何らかの理由で存在しない値というケース。
Rustはこれに対応するため、値が存在するか、または存在しないという概念を表現できる列挙型がある。
これはOption<T>であり、標準ライブラリによって定義されている。

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

Optionプレフィックス無しでSomeとNoneを使用できる。
Option<T>列挙型は通常の列挙型であり、Some(T)とNoneはOption<T>型の値である。

```rust
    let some_number = Some(5);
    let some_string = Some("a string");

    let absent_number: Option<i32> = None;
```

Noneを使用する場合、コンパイラはNone値のみを調べて、Someが保持する値の型を推測できないため、Option<T>の型を通知する必要がある。
Some値がある場合値が存在し、その値がSome内に保持されていることが分かる。
Noneの場合、ある意味Nullと同じ意味となる。つまり、有効な値がない。
Option<T>はT(任意の型)とは異なる型であるため、Option<T>値を確実に有効な値であるかのように使用するのを許可しない。
例えば、以下の場合は、Option<i8>にi8を設定しているため、コンパイル出来ない。

```rust
    let x: i8 = 5;
    let y: Option<i8> = Some(5);

    let sum = x + y;
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/624329/e74fcaa6-e974-9d58-46c2-29ba7e711a3f.png)

これはOption<T>をTに変換する必要がある。