Rust勉強中 - その16 -> 列挙とパターン

# 自己紹介
出田 守と申します。
しがないPythonプログラマです。
情報セキュリティに興味があり現在勉強中です。CTFやバグバウンティなどで腕を磨いています。主に低レイヤの技術が好きで、そっちばかり目が行きがちです。

Rustを勉強していくうえで、読んで学び、手を動かし、記録し、楽しく学んでいけたらと思います。

# 環境
新しい言語を学ぶということで、普段使わないWindowsとVimという新しい開発環境で行っています。
OS: Windows10 Home 64bit 1903
CPU: Intel Core i5-3470 @ 3.20GHz
Rust: 1.38.0
RAM: 8.00GB
Editor: Vim 8.1.1
Terminal: PowerShell

# 前回
前回は構造体について学びました。
[Rust勉強中 - その15](https://qiita.com/deta-mamoru/items/2c5179e7c8d9e0be06cb)

# 列挙型
単純な列挙型は以下のように書きます。

```Rust
enum Enum {
    Variant1,
    Variant2,
    ...
}
```
列挙型に列挙した値をvariant(ヴァリアント)といいます。構造体は、値のすべてで構成されているのに対して、列挙型はこのヴァリアントのいずれかの値をとります。つまり、私の理解ですが、列挙型とは値に対して分かりやすい名前を付けた型だと思っています。(間違っていたらすみません)
以下に列挙型の例を載せます。

```Rust
enum Binary {
    Black,
    White
}
fn binary(a: u8) -> Binary {
    if a<128 {
        Binary::Black
    } else {
        Binary::White
    }
}

fn main() {
    println!("{:?}", binary(25));  // Black
    println!("{:?}", binary(225)); // White
}
```
このような列挙型はメモリ上で整数として表現されるようです。この整数を指定することができます。もし、指定しない場合、0から順番に割り当てられます。

```Rust
enum GoodsId {
    Cup      = 10000,
    Mouse    = 218,
    Bolt     = 508
}
```
上記のような単純な列挙型は、整数型へキャストできます。

```Rust
fn main() {
    println!("{}",   GoodsId::Cup as usize);     // 10000
} 
```
また、その逆へのキャストは`as`ではなく、match式を用いて行います。

```Rust
fn cast_goods_from_id(id: usize) -> Option<GoodsId> {
    match id {
        10000 => Some(GoodsId::Cup),
        218   => Some(GoodsId::Mouse),
        508   => Some(GoodsId::Bolt),
        _     => None
    }
}

fn main() {
    ...
    println!("{:?}", cast_goods_from_id(10000)); // Some(Cup)
    println!("{:?}", cast_goods_from_id(218));   // Some(Mouse)
    println!("{:?}", cast_goods_from_id(508));   // Some(Bolt)
    println!("{:?}", cast_goods_from_id(0));     // None
    println!("{:?}", cast_goods_from_id(10000).unwrap() as usize); // 10000
}
```
ここまでの列挙型をprintln!で表示していますが、実は#[derive]属性を付けて標準的なトレイトを実装しています。

```Rust
#[derive(Debug)]
enum Binary {
    Black,
    White
}
...
#[derive(Debug)]
enum GoodsId {
    Cup      = 10000,
    Mouse    = 218,
    Bolt     = 508
}
impl GoodsId {
    pub fn price(self) -> Option<usize> {
        match self {
            GoodsId::Cup   => Some(1280),
            GoodsId::Mouse => Some(2800),
            GoodsId::Bolt  => Some(99999),
        }
    }
}

fn main() {
    ...
    println!("{}",   GoodsId::Cup.price().unwrap());   // 1280
    println!("{}",   GoodsId::Mouse.price().unwrap()); // 2800 
    println!("{}",   GoodsId::Bolt.price().unwrap());  // 99999 
}
```
さらに、構造体と同じように、implブロックでメソッドを定義できます。

Rustの列挙型では3種類のデータ(タプル型、構造体型、ユニット型)を持つvariantを定義できます。あまり良い例ではないかもしれませんが、以下のような感じで定義していきます。

## タプル型
```Rust
#[derive(Debug)]
enum Room {
    ARoom(String, u16),
    BRoom(String, u16)
}

fn main {
    ...
    let a_101 = Room::ARoom(String::from("A"), 101);
    let b_302 = Room::BRoom(String::from("B"), 302);
    println!("{:?}", a_101); // ARoom("A", 101);
    println!("{:?}", b_302); // BRoom("B", 302);
}
```

## 構造体型
```Rust
#[derive(Debug)]
enum Operate {
    Swap {a: String, b: String},
    Move {from: u8, to: u8},
    Open {path: String}
}

fn main() {
    ...
    let swap_string   = Operate::Swap {a: String::from("Rust"), b: String::from("Python")};
    let move_zero     = Operate::Move {from: 0, to: 100};
    let open_tmp_file = Operate::Open {path: String::from("tmp.txt")};
    println!("{:?}", swap_string);   // Swap { a: "Rust", b: Python }
    println!("{:?}", move_zero);     // Move { from: 0, to: 100 }
    println!("{:?}", open_tmp_file); // Open { path: "tmp.txt" }
    // println!("{}", swap_string.a); // Error: no field 'a' on type 'Operate'
}
```

## ジェネリック列挙型
列挙型でもジェネリックにできます。

```Rust
#[derive(Debug)]
enum GenericEnum<T> {
    Variant(T)
}
```
このとき、型TをBoxなどのスマートポインタにするとGenericEnum分のタグフィールドが削除されるそうです。

```Rust
fn main() {
    use std::mem::size_of;
    println!("{}=={}", size_of::<Box<i32>>(), size_of::<GenericEnum<Box<i32>>>()); // 8==8
}
```

# パターンとパターンマッチ
列挙型のフィールドに直接アクセスしようとするとエラーを吐きます。

```Rust
#[derive(Debug)]
enum Level {
    Stay,
    LevelUp   {n: i32},
    LevelDown {n: i32},
    MyLevel   (i32)
}

fn main() {
    let my_level   = Level::MyLevel (5);

    println!("my_level = {:?}", my_level.0); // no field `0` on type `Level`
```
こういった場合はmatch式を用いたパターンマッチをして、フィールドにアクセスします。match式は以下のようなフォーマットになります。

```Rust
match value {
    pattern => expr,
    ...
}
```
valueがpatternにマッチした場合、exprを実行します。

```Rust
...
impl Level {
    fn get_level(&self) -> Option<i32> {
        match *self {
            Level::Stay => None,
            Level::LevelUp {n: up} => Some(up),
            Level::LevelDown {n: down} => Some(down),
            Level::MyLevel(my_level) => Some(my_level)
        }
    }
}

fn main() {
    let stay       = Level::Stay;
    let level_up   = Level::LevelUp {n: 1};
    let level_down = Level::LevelDown {n: -1};
    let my_level   = Level::MyLevel (5);

    // println!("my_level = {:?}", my_level.0); // no field `0` on type `Level`
    println!("my_level   = {:?}", my_level.get_level());   // Some(5)
    println!("level_down = {:?}", level_down.get_level()); // Some(1)
    println!("level_up   = {:?}", level_up.get_level());   // Some(-1)
    println!("stay       = {:?}", stay.get_level());       // None
```

このほか、パターンの書き方がいくつかあります。

## リテラル
これまでも何回か見ましたが、リテラルにマッチさせる書き方は以下です。

```Rust
fn main() {
    ...
    // match: literals
    let code = 100;
    match code {
        0 => println!("success!"),
        1 => println!("error!"),
        n => println!("code:{} is unknown...", n)
    }
}
```
パターンのnはcodeが任意の値をとった場合にマッチします。
マッチした値は、コピー可能な値ならローカル変数(この場合変数n)にコピーされます。**コピー不可であれば、所有権を移動**します。

## ワイルドカード
マッチした値を無視したい場合に、ワイルドカード(_)が使えます。

```Rust
fn main() {
    ...
    // match: wildcard
    let goods = "key";
    match goods {
        "keyboard" => println!("On the 5th floor"),
        "keypad"   => println!("On the 2nd floor"),
        "key"      => println!("On the 99th floor"),
        _          => println!("Please go out")
    }
}
```
`_`は、変数を指定した場合と似ていますが、マッチした値を捨てることができます。

## タプル
対象がタプルの場合の書き方です。

```Rust
fn main() {
    ...
    // match: tuple
    let password = ("ticktack", "years");
    match password {
        ("ticktack", "days")  => println!("who?"),
        (_, "months")         => println!("he?"),
        ("ticktick", "years") => println!("her?"),
        (_, _)                => println!("Accept! (weak! weak! weak!)")
    }
}
```
## 構造体
構造体をマッチさせる場合、3種類の書き方があります。
素直に書くと以下のようになります。

```Rust
fn main() {
    ...
    // match: struct
    struct Dim {
        id: i32,
        x: i32,
        y: i32,
        z: i32
    }
    let d = Dim {id: 0, x: 0, y: 0, z: 0};
    match d {
         Dim {id: id, x: 0, y: 0, z: 0} => println!("id={}", id),
        _ => println!("Unknown...")

    }
}
```
パターンには{フィールド名: 値}を、淡々と書きます。
もし、フィールドが多いと大変なので、省略する書き方ができます。

```Rust
fn main() {
    ...
    // match: struct
    struct Dim {
        id: i32,
        x: i32,
        y: i32,
        z: i32
    }
    let d = Dim {id: 0, x: 0, y: 0, z: 0};
    match d {
        Dim {id, x, y, z} => println!("id={}", id),
        _ => println!("Unknown...")
    }
}
```
さらに、上記の場合はidのみほしいので、それ以降のフィールドを`..`を使って省略することもできます。

```Rust
fn main() {
    ...
    // match: struct
    struct Dim {
        id: i32,
        x: i32,
        y: i32,
        z: i32
    }
    let d = Dim {id: 0, x: 0, y: 0, z: 0};
    match d {
        Dim {id, ..} => println!("id={}", id),
        _ => println!("Unknown...")
    }
}
```

## 参照
先ほどマッチしたコピー不可の値はローカル変数に所有権を移動させると書きました。以下はString型をマッチさせる例です。

```Rust
fn main() {
    ...
    // match: reference
    let mut string = String::from("String!");
    match string {
        s => {
            println!("s={}", s);
            println!("string.len()={}", string.len());
        }
    }
}
```
```
error[E0382]: borrow of moved value: `string`
  --> .\src\main.rs:74:41
   |
70 |     let mut string = String::from("String!");
   |         ---------- move occurs because `string` has type `std::string::String`, which does not implement the `Copy
 trait
71 |     match string {
72 |         s => {
   |         - value moved here
73 |             println!("s={}", s);
74 |             println!("string.len()={}", string.len());
   |                                         ^^^^^^ value borrowed here after move

error: aborting due to previous error
```
この場合、変数sに所有権を移動させるので変数stringがドロップされます。そのドロップされた変数stringにアクセスしようとするのでエラーを吐きます。
このような場合は、値を借用すれば解決します。借用したい場合は、`ref`を使います。

```Rust
fn main() {
    ...
    // match: reference
    let mut string = String::from("String!");
    match string {
        ref s => {
            println!("s={}", s);
            println!("string.len()={}", string.len());
        }
    }
}
```
話が逸れるのですが、Stringをマッチさせたい場合は以下のような書き方もできます。

```Rust
fn main() {
    ...
    match &*string {
        "String!" => println!("String!"),
        _         => println!("Unknown...")
    }
}
```
これは、stringを参照解決して、strにし、その借用&strにして、マッチさせます。このようにしないと、String型と&str型とのパターンマッチとなり型が異なるためエラーを吐きます。
このほかに、後述するガードを使うことでもString型をマッチさせることができます。

話を戻して、可変参照にしたい場合は以下のようにします。

```Rust
fn main() {
    ...
    match string {
        ref mut s => {
            println!("s={}", s);
            println!("s.make_ascii_uppercase={}", {s.make_ascii_uppercase(); s});
        }
    }
}
```
このようにすれば、変数sに対して変更を加えることができます。

参照自体をマッチさせたい場合は&演算子を使います。

```Rust
fn main() {
    ...
    let x = &0;
    match x {
        &0 => println!("zero"),
        &1 => println!("one"),
        _ => println!("Unknown...")
    }
}
```

## or
`|`を使えば、orのようにパターンを並べられます。

```Rust
fn main() {
    ...
    let password = "password!";
    match password {
        "password!"|"p@ssw0rd!"|"PASSWORD!" => println!("Accept!"),
        _ => println!("who?")
    }
}
```

## 範囲
`..=`を使えば範囲を指定できます。この場合、`..`と違って、終点の値が含まれます。

```Rust
fn main() {
    ...
    let z = 'z';
    match z {
        '0'..='9' => println!("digit"),
        'a'..='z' => println!("char"),
        _ => println!("?")
    }
}
```

## ガード
ガードは`pattern if ~`でif文がtrueの場合だけpatternにマッチします。値が移動する場合、ガードを付けることができません。この場合は、`ref`を使って借用することで解決します。

```Rust
fn main() {
    ...
    // match: guard
    let string = String::from("String!");
    match string {
        ref s if s=="String" => println!("s={}", s),
        ref s if s=="String!" => println!("s={}", s),
        _ => println!("Unknown...")
    }
}
```

## \@
@パターンを使うことでパターンにマッチした場合のみ、ローカル変数に値を格納します。

```Rust
fn main() {
    ...
    // match: @
    let digit = 10;
    match digit {
        d @ 0..=9 => println!("{} is ok!", d),
        d @ 10..=100 => println!("{} is too much!", d),
        _ => println!("who?")
    }
}
```

# ソース
```Rust:enums
#[allow(unused)]
fn get_type<T>(_: T) -> &'static str {
    std::any::type_name::<T>()
}

#[derive(Debug)]
enum Binary {
    Black,
    White
}
fn binary(a: u8) -> Binary {
    if a<128 {
        Binary::Black
    } else {
        Binary::White
    }
}

#[derive(Debug)]
enum GoodsId {
    Cup      = 10000,
    Mouse    = 218,
    Bolt     = 508
}
fn cast_goods_from_id(id: usize) -> Option<GoodsId> {
    match id {
        10000 => Some(GoodsId::Cup),
        218   => Some(GoodsId::Mouse),
        508   => Some(GoodsId::Bolt),
        _     => None
    }
}
impl GoodsId {
    pub fn price(self) -> Option<usize> {
        match self {
            GoodsId::Cup   => Some(1280),
            GoodsId::Mouse => Some(2800),
            GoodsId::Bolt  => Some(99999),
        }
    }
}

#[derive(Debug)]
enum Room {
    ARoom(String, u16),
    BRoom(String, u16)
}

#[derive(Debug)]
enum Operate {
    Swap {a: String, b: String},
    Move {from: u8, to: u8},
    Open {path: String}
}

#[derive(Debug)]
enum GenericEnum<T> {
    Variant(T)
}

fn main() {
    println!("{:?}", binary(25));  // Black
    println!("{:?}", binary(225)); // White

    println!("{}",   GoodsId::Cup as usize);     // 10000
    println!("{:?}", cast_goods_from_id(10000)); // Some(Cup)
    println!("{:?}", cast_goods_from_id(218));   // Some(Mouse)
    println!("{:?}", cast_goods_from_id(508));   // Some(Bolt)
    println!("{:?}", cast_goods_from_id(0));     // None
    println!("{:?}", cast_goods_from_id(10000).unwrap() as usize); // 10000

    println!("{}",   GoodsId::Cup.price().unwrap());   // 1280
    println!("{}",   GoodsId::Mouse.price().unwrap()); // 2800 
    println!("{}",   GoodsId::Bolt.price().unwrap());  // 99999 

    let a_101 = Room::ARoom(String::from("A"), 101);
    let b_302 = Room::BRoom(String::from("B"), 302);
    println!("{:?}", a_101); // ARoom("A", 101);
    println!("{:?}", b_302); // BRoom("B", 302);

    let swap_string   = Operate::Swap {a: String::from("Rust"), b: String::from("Python")};
    let move_zero     = Operate::Move {from: 0, to: 100};
    let open_tmp_file = Operate::Open {path: String::from("tmp.txt")};
    println!("{:?}", swap_string);   // Swap { a: "Rust", b: Python }
    println!("{:?}", move_zero);     // Move { from: 0, to: 100 }
    println!("{:?}", open_tmp_file); // Open { path: "tmp.txt" }
    // println!("{}", swap_string.a); // Error: no field 'a' on type 'Operate'

    use std::mem::size_of;
    println!("{}=={}", size_of::<Box<i32>>(), size_of::<GenericEnum<Box<i32>>>()); // 8==8
}
```
```
Black
White
10000
Some(Cup)
Some(Mouse)
Some(Bolt)
None
10000
1280
2800
99999
ARoom("A", 101)
BRoom("B", 302)
Swap { a: "Rust", b: "Python" }
Move { from: 0, to: 100 }
Open { path: "tmp.txt" }
8==8
```
```Rust:pattern
#[derive(Debug)]
enum Level {
    Stay,
    LevelUp   {n: i32},
    LevelDown {n: i32},
    MyLevel   (i32)
}
impl Level {
    fn get_level(&self) -> Option<i32> {
        match *self {
            Level::Stay => None,
            Level::LevelUp {n: up} => Some(up),
            Level::LevelDown {n: down} => Some(down),
            Level::MyLevel(my_level) => Some(my_level)
        }
    }
}

fn main() {
    let stay       = Level::Stay;
    let level_up   = Level::LevelUp {n: 1};
    let level_down = Level::LevelDown {n: -1};
    let my_level   = Level::MyLevel (5);

    // println!("my_level = {:?}", my_level.0); // no field `0` on type `Level`
    println!("my_level   = {:?}", my_level.get_level());   // Some(5)
    println!("level_down = {:?}", level_down.get_level()); // Some(1)
    println!("level_up   = {:?}", level_up.get_level());   // Some(-1)
    println!("stay       = {:?}", stay.get_level());       // None

    // match: literals
    let code = 100;
    match code {
        0 => println!("success!"),
        1 => println!("error!"),
        n => println!("code:{} is unknown...", n)
    }
    // match: wildcard
    let goods = "key";
    match goods {
        "keyboard" => println!("On the 5th floor"),
        "keypad"   => println!("On the 2nd floor"),
        "key"      => println!("On the 99th floor"),
        _          => println!("Please go out")
    }
    // match: tuple
    let password = ("ticktack", "years");
    match password {
        ("ticktack", "days")  => println!("who?"),
        (_, "months")         => println!("he?"),
        ("ticktick", "years") => println!("her?"),
        (_, _)                => println!("Accept! (weak! weak! weak!)")
    }
    // match: struct
    struct Dim {
        id: i32,
        x: i32,
        y: i32,
        z: i32
    }
    let d = Dim {id: 0, x: 0, y: 0, z: 0};
    match d {
        // Dim {id: id, x: 0, y: 0, z: 0} => println!("id={}", id),
        Dim {id, x, y, z} => println!("id={}", id),
        // Dim {id, ..} => println!("id={}", id),
        _ => println!("Unknown...")

    }
    // match: reference
    let mut string = String::from("String!");
    match string {
        ref s => {
            println!("s={}", s);
            println!("string.len()={}", string.len());
        }
    }
    match &*string {
        "String!" => println!("String!"),// Error: mismatched types
        _         => println!("Unknown...")
    }
    match string {
        ref mut s => {
            println!("s={}", s);
            println!("s.make_ascii_uppercase={}", {s.make_ascii_uppercase(); s});
        }
    }
    println!("string = {}", string);

    let x = &0;
    match x {
        &0 => println!("zero"),
        &1 => println!("one"),
        _ => println!("Unknown...")
    }

    // match: range
    let password = "password!";
    match password {
        "password!"|"p@ssw0rd!"|"PASSWORD!" => println!("Accept!"),
        _ => println!("who?")
    }
    let z = 'z';
    match z {
        '0'..='9' => println!("digit"),
        'a'..='z' => println!("char"),
        _ => println!("?")
    }

    // match: guard
    let string = String::from("String!");
    match string {
        ref s if s=="String" => println!("s={}", s),
        ref s if s=="String!" => println!("s={}", s),
        _ => println!("Unknown...")
    }

    // match: @
    let digit = 10;
    match digit {
        d @ 0..=9 => println!("{} is ok!", d),
        d @ 10..=100 => println!("{} is too much!", d),
        _ => println!("who?")
    }
}
```
```
my_level   = Some(5)
level_down = Some(-1)
level_up   = Some(1)
stay       = None
code:100 is unknown...
On the 99th floor
Accept! (weak! weak! weak!)
id=0
s=String!
string.len()=7
String!
s=String!
s.make_ascii_uppercase=STRING!
string = STRING!
zero
Accept!
char
s=String!
10 is too much!
```

今回はここまでー。
コード例を考えるのに時間が取られるんです。しかも、ピンとくる例でもないんです。大変なんです。