【Effective-Rustlings-jp】Day 1：型システムを使ってデータ構造を再現しよう

# はじめに
こんにちは。細々とプログラミングをしている[sotanengel](https://x.com/sota_eng_prog)です。
この記事は以下の記事の連載です。

https://qiita.com/sotanengel/private/78103dcfe2fa34f8815e

<details>
<summary><strong>他の連載記事</strong> (<em>詳細</em>)</summary>

- [Day 1：型システムを使ってデータ構造を再現しよう](https://qiita.com/sotanengel/private/588a7f7cc03c36fc1adc)
- [Day 2：型システムを用いて共通の挙動を表現しよう](https://qiita.com/sotanengel/private/2a8fc62b6d9f8e916021)
- [Day 3：OptionとResultに対してはmatchを用いずに変換しよう](https://qiita.com/sotanengel/items/b54990dc82d877b9d70d)
- [Day 4：標準のErrorを使おう](https://qiita.com/sotanengel/private/79f683c3d75d09244567)
- [Day 5：型変換を理解しよう](https://qiita.com/sotanengel/private/8cc28c8b6108e5998e23)
- [Day 6：newtypeパターンを活用しよう](https://qiita.com/sotanengel/private/38814f8b5a88aad9eccc)
- [Day 7：複雑な型にはビルダを使おう](https://qiita.com/sotanengel/private/80bc555e95ed56b72f14)
- [Day 8：明示的なループの代わりにイテレータ変換を使用することを検討しよう](https://qiita.com/sotanengel/private/f7f4fc03f0b2dc445bf2)
- [Day 9：標準トレイトに習熟しよう](https://qiita.com/sotanengel/private/24a8ad443fc23e779ccd)
- [Day 10：RIIパターンにはDropトレイトを実装しよう](https://qiita.com/sotanengel/private/a3d1b04f98ea80b8989d)
- [Day 11：ジェネリクスとトレイトオブジェクトのトレードオフを理解しよう](https://qiita.com/sotanengel/private/8e2e88d5619b434c3ada)
- [Day 12：デフォルト実装を用いて、実装しなければならないトレイトメソッドを最小限にしよう](https://qiita.com/sotanengel/private/16b02d32732727eaf66f)
- [Day 13：Don't panic](https://qiita.com/sotanengel/private/aa833ce99c8e8b8eecf2)
- [Day 14：リフレクションを避けよう](https://qiita.com/sotanengel/private/6108b22f6644d4fde997)
- [Day 15：可視範囲を最小化しよう](https://qiita.com/sotanengel/private/4075925e05de50dfac96)
- [Day 16：ワイルドカードインポートを避けよう](https://qiita.com/sotanengel/private/555314f77c8318bb1c45)
- [Day 17：パブリックインターフェースのドキュメントを書こう](https://qiita.com/sotanengel/private/b4e8a11522b253ebb62e)
- [Day 18：分別を持ってマクロを使おう](https://qiita.com/sotanengel/private/a33b5ab66a82a26b4adc)
- [Day 19：Clippyに耳を傾けよう](https://qiita.com/sotanengel/private/b4c161bb87fc9e7e813e)
- [Day 20：ユニットテスト以上のものを書こう](https://qiita.com/sotanengel/private/58fcafaee5418b2b2b16)

</details>

また本記事は[Effective Rust(David Drysdale (著), 中田 秀基 (翻訳))](https://www.oreilly.com/library/view/effective-rust/9781098151393/)を参考に作成されております。とてもいい書籍ですので興味を持った方は、ぜひ読んでみてください！
<div style="text-align: center;">
  <figure>
    <a href="https://www.amazon.co.jp/Effective-Rust-%E2%80%95Rust%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E6%94%B9%E5%96%84%E3%81%97%E3%80%81%E3%82%A8%E3%82%B3%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%82%92%E6%9C%80%E5%A4%A7%E9%99%90%E3%81%AB%E6%B4%BB%E7%94%A8%E3%81%99%E3%82%8B%E3%81%9F%E3%82%81%E3%81%AE35%E9%A0%85%E7%9B%AE-David-Drysdale/dp/4814400942"  target="_blank">
      <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1758960/cb1245be-02d6-0c74-8b79-e46288f2ea73.png" alt="商品画像" width="50%">
    </a>
  </figure>
</div>

# 今日の内容
## 概要
Rustの強みとして、強力なデータ型がいくつも標準で実装されています。
これらを使いこなすことでコンパイル時にエラーを発見し、開発速度を向上させましょう。

## 適切な型を選択して、暗黙的な型変換を防止しよう
### 問
`x`はi32型です。
それよりもサイズの大きいi64型の変数`y`に`x`を代入するにはどうすればいいでしょうか？

```rust
fn main() {
    let x: i32 = 42;
    let y: i64 = x; // TODO: 抜けているキーワードを追加しましょう。
    println!("{}", y);
}
```

### 解答
サイズが明らかに大きい変数への代入でもRustでは厳格なルールのもとに型を変換します。
したがって、Rustでは変数の型を厳密に定義することでバグの混入を防ぐ確率が高くなります。

```rust
fn main() {
    let x: i32 = 42;
    let y: i64 = x.into(); // into で型変換を行います。
    println!("{}", y);
}
```

## Enumで関数への代入を正確に行う
### 問([リンク](https://github.com/sotanengel/effective-rustlings-jp/blob/20250101/exercises/1_types_Use_the_type_system_to_express_your_data_structures/2_enum_1.rs))
コンパイルエラーが起きている部分を修正しましょう。

<details>
<summary><strong>コード</strong> (<em>詳細</em>)</summary>

```rust
enum Apple {
    Price(u32),
}

enum Meat {
    Price(u32),
}

// AppleとMeatを引数にとる関数
fn print_price(apple: Apple, meat: Meat) {
    match apple {
        Apple::Price(price) => println!("Apple price: {} yen", price),
    }
    match meat {
        Meat::Price(price) => println!("Meat price: {} yen", price),
    }
}

fn main() {
    let apple = Apple::Price(300);
    let meat = Meat::Price(1200);

    print_price(meat, apple); // TODO: Enumを使うことであるエラーをコンパイル時に特定できます。
}
```

</details>

### 解答([リンク](https://github.com/sotanengel/effective-rustlings-jp/blob/20250101/solutions/1_types_Use_the_type_system_to_express_your_data_structures/2_enum_1.rs))
Enumで異なるデータを定義することで、コンパイル時に引数の渡し方のミスを検知することができます。

<details>
<summary><strong>コード</strong> (<em>詳細</em>)</summary>

```rust
enum Apple {
    Price(u32),
}

enum Meat {
    Price(u32),
}

// AppleとMeatを引数にとる関数
fn print_price(apple: Apple, meat: Meat) {
    match apple {
        Apple::Price(price) => println!("Apple price: {} yen", price),
    }
    match meat {
        Meat::Price(price) => println!("Meat price: {} yen", price),
    }
}

fn main() {
    let apple = Apple::Price(300);
    let meat = Meat::Price(1200);

    print_price(apple, meat); // Enumを使うことで引数の順番のミスを事前に検知できます。
}
```

</details>

## Enumにmatchを使って抜け漏れを防止しよう
### 問([リンク](https://github.com/sotanengel/effective-rustlings-jp/blob/20250101/exercises/1_types_Use_the_type_system_to_express_your_data_structures/2_enum_2.rs))
Enumに対するmatch文のエラーを解消してください。

<details>
<summary><strong>コード</strong> (<em>詳細</em>)</summary>

```rust
enum TrafficLight {
    Red,
    Yellow,
    Green,
    Blinking,
}

fn signal_action(light: TrafficLight) {
    match light {
        // TODO: コンパイルエラーを解消してください。
        TrafficLight::Red => println!("停止してください"),
        TrafficLight::Green => println!("進んでください"),
    }
}

fn main() {
    let light = TrafficLight::Yellow;
    signal_action(light);
}
```

</details>

### 解答([リンク](https://github.com/sotanengel/effective-rustlings-jp/blob/20250101/solutions/1_types_Use_the_type_system_to_express_your_data_structures/2_enum_2.rs))
Enumとmatchを組み合わせることで、条件の抜け漏れをコンパイル時に検知することができます。

<details>
<summary><strong>コード</strong> (<em>詳細</em>)</summary>

```rust
#[allow(dead_code)]
enum TrafficLight {
    Red,
    Yellow,
    Green,
    Blinking,
}

// 信号の状態に応じてアクションを決定する関数
fn signal_action(light: TrafficLight) {
    match light {
        TrafficLight::Red => println!("停止してください"), // 赤信号では停止
        TrafficLight::Yellow => println!("注意してください"), // 黄色信号では注意
        TrafficLight::Green => println!("進んでください"), // 緑信号では進行
        TrafficLight::Blinking => println!("点滅中 - 注意して進んでください"), // 点滅信号の処理を追加
    }
}

fn main() {
    let light = TrafficLight::Yellow;
    signal_action(light); // 黄色信号をテスト
}
```

</details>

## 構造体とEnumを組み合わせよう
### 問([リンク](https://github.com/sotanengel/effective-rustlings-jp/blob/20250101/exercises/1_types_Use_the_type_system_to_express_your_data_structures/2_enum_3.rs))
`TrafficLight`という型を使って、コンパイルエラーを解消してください。

<details>
<summary><strong>コード</strong> (<em>詳細</em>)</summary>

```rust
// 信号の状態を表すEnum
enum TrafficLight {
    Red,
    Yellow,
    Green,
}

// 車両を表す構造体
// TODO:先ほどまでの問題のTrafficLightを参考にcurrent_lightに適切な型を挿入しなさい。
struct Vehicle {
    model: String,
    current_light: String, // 信号状態を保持するフィールド("Red"か"Yellow"、"Green"が入る)
}

// 信号の状態に応じて車両のアクションを決める関数
fn drive(vehicle: &Vehicle) {
    match vehicle.current_light {
        TrafficLight::Red => println!("{} は停止しています。", vehicle.model),
        TrafficLight::Yellow => println!("{} は注意しています。", vehicle.model),
        TrafficLight::Green => println!("{} は進んでいます。", vehicle.model),
    }
}

fn main() {
    // 車両インスタンスを作成し、信号を設定
    let car = Vehicle {
        model: String::from("Toyota Prius"),
        current_light: "Red", // 赤信号
    };

    drive(&car); // 実行すると「Toyota Prius は停止しています。」と表示
}
```

</details>

### 解答([リンク](https://github.com/sotanengel/effective-rustlings-jp/blob/20250101/solutions/1_types_Use_the_type_system_to_express_your_data_structures/2_enum_3.rs))
Enumとstructを用いることで、構造体のフィールドの要素を柔軟に表現することができます。
特に今回の場合、問題文では`current_light`フィールドにコメントでフィールド条件を記載していましたが、このようなコードの管理方法ではコメントの管理が行われなくなった時にバグが混入する原因となります。
Rustの優秀な型表現を使って、バグの混入を防ぎましょう。

<details>
<summary><strong>コード</strong> (<em>詳細</em>)</summary>

```rust
// 信号の状態を表すEnum
#[allow(dead_code)]
enum TrafficLight {
    Red,
    Yellow,
    Green,
}

// 車両を表す構造体
struct Vehicle {
    model: String,
    current_light: TrafficLight, // 信号状態をTrafficLightというEnumで定義することで「有効な値の組み合わせ」だけを格納できるようにしよう。
}

// 信号の状態に応じて車両のアクションを決める関数
fn drive(vehicle: &Vehicle) {
    match vehicle.current_light {
        TrafficLight::Red => println!("{} は停止しています。", vehicle.model),
        TrafficLight::Yellow => println!("{} は注意しています。", vehicle.model),
        TrafficLight::Green => println!("{} は進んでいます。", vehicle.model),
    }
}

fn main() {
    // 車両インスタンスを作成し、信号を設定
    let car = Vehicle {
        model: String::from("Toyota Prius"),
        current_light: TrafficLight::Red, // 赤信号
    };

    drive(&car); // 実行すると「Toyota Prius は停止しています。」と表示
}
```

</details>

# さいごに

もしも本リポジトリで不備などあれば、リポジトリのissueやPRなどでご指摘いただければと思います。

https://github.com/sotanengel/effective-rustlings-jp/issues