The Rust Programming Languageを読み進める（6章）

# はじめに

The Rust Programming Languageを頭から読み進めてみる。読みながら要点をまとめていった記事です。

* [The Rust Programming Languageを読み進める（1〜2章）](https://qiita.com/yktooo/items/df17496b38b1b5c49aba)
* [The Rust Programming Languageを読み進める（3章）](https://qiita.com/yktooo/items/481d49f248cb9947af81)
* [The Rust Programming Languageを読み進める（4章）](https://qiita.com/yktooo/items/e0d502867e16ce91923a)
* [The Rust Programming Languageを読み進める（5章）](https://qiita.com/yktooo/items/8e5890c235ed9f5a6b4b)

# 6章：Enumとパターンマッチング
## 6.1章：Enumを定義する
https://doc.rust-jp.rs/book-ja/ch06-01-defining-an-enum.html
### Enumの基本的な使い方

```rust
// enum
#[derive(Debug)]
enum IpAddrKind {
    V4,
    V6,
}

// 構造体
#[derive(Debug)]
struct IpAddr {
    kind: IpAddrKind,
    address: String,
}

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

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

    println!("{:?}", home);
    println!("{:?}", loopback);
}
```
```
IpAddr { kind: V4, address: "127.0.0.1" }
IpAddr { kind: V6, address: "::1" }
```
* よくある形のenum定義
* 他の言語だと`列挙型の名前.値`という書き方を見ることもあるが、Rustの場合は`列挙型の名前::値`でアクセス可能

### Enumに値を直接定義
```rust
#[derive(Debug)]
enum IpAddrDirect {
    V4(String),
    V6(String),
}

#[derive(Debug)]
enum IpAddrDiff {
    V4(u8, u8, u8, u8),
    V6(String),
}

fn main() {
    let home = IpAddrDirect::V4(String::from("127.0.0.1"));
    let loopback = IpAddrDirect::V6(String::from("::1"));

    println!("{:?}", home);
    println!("{:?}", loopback);

    let home = IpAddrDiff::V4(127, 0, 0, 1);
    let loopback = IpAddrDiff::V6(String::from("::1"));

    println!("{:?}", home);
    println!("{:?}", loopback);
}
```
```
V4("127.0.0.1")
V6("::1")
V4(127, 0, 0, 1)
V6("::1")
```
* 列挙子に直接データを定義することが可能
    * 例：`V4(String)`に対して、`IpAddrDirect::V4(String::from("..."))`
* `IpAddrDirect`は、各列挙子に直接データを定義している例で、余計に構造体を作らなくても良くなっている
* また、**各列挙子に紐付けるデータの型は異なっていても良く**、`IpAddrDiff`のように異なる型の値を定義することが可能

### Enumに構造体を定義
```rust
#[derive(Debug)]
enum IpAddr {
    V4(Ipv4Addr),
    V6(Ipv6Addr),
}

#[derive(Debug)]
struct Ipv4Addr {
    address: String,
    maxbit: i32,
}

#[derive(Debug)]
struct Ipv6Addr {
    address: String,
    maxbit: i32,
}

fn main() {
    let ipv4 = IpAddr::V4(Ipv4Addr {
        address: String::from("127.0.0.1"),
        maxbit: 32,
    });

    let ipv6 = IpAddr::V6(Ipv6Addr {
        address: String::from("2001:0000:130F:0000:0000:09C0:876A:130B"),
        maxbit: 128,
    });
    println!("{:?}", ipv4);
    println!("{:?}", ipv6);
}
```
```
V4(Ipv4Addr { address: "127.0.0.1", maxbit: 32 })
V6(Ipv6Addr { address: "2001:0000:130F:0000:0000:09C0:876A:130B", maxbit: 128 })
```
* enumの列挙子に数字や文字を格納できる例を1つ前に示したが、構造体も格納することが可能

### enumとstructの比較
```rust
enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}
```
```rust
struct QuitMessage; // ユニット構造体
struct MoveMessage {
    x: i32,
    y: i32,
}
struct WriteMessage(String); // タプル構造体
struct ChangeColorMessage(i32, i32, i32); // タプル構造体
```
* 上記2つは、enum or structで4つの機能を定義した例
* enumを使用することで下記のメリットがあり
    * 型安全：各データ型を1つの型 (`Message`) として扱える
    * 簡素化：structと比べて関数の重複を避けられる
    * 拡張性：新しい定義を追加する際は、新しいバリアントを追加するだけで済む

### implを使ったメソッドの定義
```rust
#[derive(Debug)]
enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}

impl Message {
    fn call(&self) {
        println!("{:?}", &self);
    }
}

fn main() {
    let m = Message::Write(String::from("hello"));
    let n = Message::Move{x: 10, y: 20};

    m.call();
    n.call();
}
```
```
Write("hello")
Move { x: 10, y: 20 }
```
* 構造体と同様に、enumでも`impl`を使ってメソッドを定義可能
* `Message`の列挙子はそれぞれ型が異なるが、`impl`の引数に`&self`が取れるため、どのような値に対しても柔軟に`println!`を呼ぶことができている

### Option型
```rust
fn is_even(number: &i32) -> Option<i32> {
    if number % 2 == 0 {
        return Some(*number);
    }
    None
}

fn main() {
    let number = 10;
    let result = is_even(&number);

    match result {
        Some(even) => println!("{} is even number", even),
        None => println!("even number not found"),
    }
}
```
```
10 is even number
```
* `Option`は、**値が存在するかどうか**を表現するenum
* Rustでは`null`が存在しないため、値が存在しないものに対しては`Option`を使用することが慣習

## 6.2章：match制御フロー演算子
https://doc.rust-jp.rs/book-ja/ch06-02-match.html

### match制御によるenum分岐
```rust
enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter,
}

fn value_in_cents(coin: Coin) -> u32 {
    match coin {
        Coin::Penny => {
            println!("Lucky penny!");
            1
        },
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter => 25,
    }
}

fn main() {
    let penny = value_in_cents(Coin::Penny);
    println!("{}", penny);
}
```
```
Lucky penny!
1
```
* enumの列挙子によって処理を行う場合、`match`を使える
* `if`の場合だとboolで判断するのに対して、`match`の場合はどのような型でも良い

### パターンマッチした値に束縛
```rust
#[derive(Debug)]
enum UsState {
    Alabama,
    Alaska,
    Carifornia,
}

enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter(UsState),
}

fn value_in_cents(coin: Coin) -> u32 {
    match coin {
        Coin::Penny => 1,
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter(state) => {
            println!("State quarter from {:?}!", state);
            25
        }
    }
}

fn main() {
    let alaska_quarter = value_in_cents(Coin::Quarter(UsState::Alaska));
    println!("{:?}", alaska_quarter);
}
```
```
State quarter from Alaska!
25
```
* `Quarter(UsState)`をenumに追加し、`match`の中の条件式に取り込んでいる
* この時、`Coin::Quarter(state)`に到達するまでマッチせず、最終的に`state`に`UsState::Alaska`が束縛される

### Optionとのマッチ
```rust
fn plus_one(x: Option<i32>) -> Option<i32> {
    match x {
        None => None,
        Some(i) => Some(i + 1),
    }
}

fn main() {
    let five = Some(5);
    println!("{:?}", five);

    let six = plus_one(five);
    println!("{:?}", six);

    let none = plus_one(None);
    println!("{:?}", none);
}
```
```
Some(5)
Some(6)
None
```
* `Option`とマッチさせ、`Some`に含まれる値を足し算して、新しい`Some`の値を生成可能
* `match`とenumは相性がよいため、頻出パターン
* **`match`は全てのパターンを書き下す必要があり**、例えば`Option`で`None`に関する定義を書かない、ということはできない

### _というプレースホルダー
```rust
fn main() {
    let some_u8_value = 0u8; // 0
    let result = match some_u8_value {
        1 => println!("one"),
        3 => println!("three"),
        5 => println!("five"),
        7 => println!("seven"),
        _ => (),
    };
    
    println!("{:?}", result);
}
```
```
()
```
* `match`で全てのパターンを列挙できないときは、`_`を使用
* 1つのパターンしか無い場合に`match`は長すぎるので、その際はこの後に述べる`if let`を用いる

## 6.3章：if letによる制御フロー
https://doc.rust-jp.rs/book-ja/ch06-03-if-let.html

```rust
#[derive(Debug)]
enum UsState {
    Alabama,
    Alaska,
    Carifornia,
}

enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter(UsState),
}

fn main() {
    let mut count = 0;
    let coin = Coin::Penny;

    // match coin {
    //     Coin::Quarter(state) => println!("State quarter from {:?}!", state),
    //     _ => count += 1,
    // }

    // 上記match式と同じの糖衣構文
    if let Coin::Quarter(state) = coin {
        println!("State quarter from {:?}!", state);
    } else {
        count += 1;
    }

    println!("count: {}", count)
}
```
```
count: 1
```
* `if let`はパターンが1つの場合に、`match`を簡略化した糖衣構文 (syntax sugar)
    * 余談：等位構文の誤字かと最初は思ったが、syntax sugar（読み書きやすく導入された構文）を糖衣構文と書く
* デメリットとしては、`match`で強制させていたパターン網羅ができない