Rust 100 Ex 🏃【8/37】 デストラクタ(変数の終わり)・トレイト ～終わりと始まり～

前の記事

- [【0】 準備](https://qiita.com/namn1125/items/bd1b4cd028874189a3c1) ← 初回
- ...
- [【7】 スタック・ヒープと参照のサイズ ～メモリの話～](https://qiita.com/namn1125/items/780d80e4340f0e34dbf6) ← 前回
- 【8】 デストラクタ(変数の終わり)・トレイト ～終わりと始まり～ ← 今回

<details><summary>全記事一覧</summary>

- [【0】 準備](https://qiita.com/namn1125/items/bd1b4cd028874189a3c1)
- [【1】 構文・整数・変数](https://qiita.com/namn1125/items/c776768baec89b8a0438)
- [【2】 if・パニック・演習](https://qiita.com/namn1125/items/13870965e8273bc87bce)
- [【3】 可変・ループ・オーバーフロー](https://qiita.com/namn1125/items/4f57f8aede2f5201016f)
- [【4】 キャスト・構造体 (たまにUFCS)](https://qiita.com/namn1125/items/227ed5bbedbb775c5d27)
- [【5】 バリデーション・モジュールの公開範囲 ～ → カプセル化！～](https://qiita.com/namn1125/items/46721cceccbfa9ec3c7a)
- [【6】 カプセル化の続きと所有権とセッター ～そして不変参照と可変参照！～](https://qiita.com/namn1125/items/442de7798d49f777384c)
- [【7】 スタック・ヒープと参照のサイズ ～メモリの話～](https://qiita.com/namn1125/items/780d80e4340f0e34dbf6)
- [【8】 デストラクタ(変数の終わり)・トレイト ～終わりと始まり～](https://qiita.com/namn1125/items/7247edde3b6bd0b85b25)
- [【9】 Orphan rule (孤児ルール)・演算子オーバーロード・derive ～Empowerment 💪 ～](https://qiita.com/namn1125/items/e565713b423301a80c76)
- [【10】 トレイト境界・文字列・Derefトレイト ～トレイトのアレコレ～](https://qiita.com/namn1125/items/9060faee2965f23e0e3d)
- [【11】 Sized トレイト・From トレイト・関連型 ～おもしろトレイトと関連型～](https://qiita.com/namn1125/items/b68990712d379a3d6a49)
- [【12】 Clone・Copy・Dropトレイト ～覚えるべき主要トレイトたち～](https://qiita.com/namn1125/items/c6f34945f331c7229746)
- [【13】 トレイトまとめ・列挙型・match式 ～最強のトレイトの次は、最強の列挙型～](https://qiita.com/namn1125/items/6b2cc31d6a47f9ffaff6)
- [【14】 フィールド付き列挙型とOption型 ～チョクワガタ～](https://qiita.com/namn1125/items/6fe69d1cab6c7936c698)
- [【15】 Result型 ～Rust流エラーハンドリング術～](https://qiita.com/namn1125/items/698aafaf22ec4bfd1bde)
- [【16】 Errorトレイトと外部クレート ～依存はCargo.tomlに全部お任せ！～](https://qiita.com/namn1125/items/9dae7b728fa0668e4102)
- [【17】 thiserror・TryFrom ～トレイトもResultも自由自在！～](https://qiita.com/namn1125/items/d67006a29b7ff5d6148c)
- [【18】 Errorのネスト・慣例的な書き方 ～Rustらしさの目醒め～](https://qiita.com/namn1125/items/705829027661a38e4a4f)
- [【19】 配列・動的配列 ～スタックが使われる配列と、ヒープに保存できる動的配列～](https://qiita.com/namn1125/items/9c49fd4c08f3e61896c7)
- [【20】 動的配列のリサイズ・イテレータ ～またまたトレイト登場！～](https://qiita.com/namn1125/items/6e4dede789c6de97f396)
- [【21】 イテレータ・ライフタイム ～ライフタイム注釈ようやく登場！～](https://qiita.com/namn1125/items/9a1224abf733f29a980a)
- [【22】 コンビネータ・RPIT ～ :dragon: 「 `Iterator` トレイトを実装してるやつ」～](https://qiita.com/namn1125/items/27fe20f2d60803c94357)
- [【23】 `impl Trait` ・スライス ～配列の欠片～](https://qiita.com/namn1125/items/7601d0268293ce7fcf28)
- [【24】 可変スライス・下書き構造体 ～構造体で状態表現～](https://qiita.com/namn1125/items/a9acc7dc2ca7c4c55cc3)
- [【25】 インデックス・可変インデックス ～インデックスもトレイト！～](https://qiita.com/namn1125/items/bd43375362af10ea8b42)
- [【26】 HashMap・順序・BTreeMap ～Rustの辞書型～](https://qiita.com/namn1125/items/a2806cded12fa719a1cb)
- [【27】 スレッド・'staticライフタイム ～並列処理に見るRustの恩恵～](https://qiita.com/namn1125/items/5a4a09172834063268b1)
- [【28】 リーク・スコープ付きスレッド ～ライフタイムに技あり！～](https://qiita.com/namn1125/items/34a0aacd4d40f9234aef)
- [【29】 チャネル・参照の内部可変性 ～Rustの虎の子、mpscと `Rc<RefCell<T>>`～](https://qiita.com/namn1125/items/861e72dbb4b837508948)
- [【30】 双方向通信・リファクタリング ～返信用封筒を入れよう！～](https://qiita.com/namn1125/items/46f03de967d0de46737e)
- [【31】 上限付きチャネル・PATCH機能 ～パンクしないように制御！～](https://qiita.com/namn1125/items/d7ae8a205ee969339401)
- [【32】 `Send`・排他的ロック(`Mutex`)・非対称排他的ロック(`RwLock`) ～真打 `Arc<Mutex<T>>` 登場～](https://qiita.com/namn1125/items/eee890dc8672e975e70c)
- [【33】 チャネルなしで実装・Syncの話 ～考察回です～](https://qiita.com/namn1125/items/cc8bee3871b727e564e3)
- [【34】 `async fn`・非同期タスク生成 ～Rustの非同期入門～](https://qiita.com/namn1125/items/9c32b730a283ad76c891)
- [【35】 非同期ランタイム・Futureトレイト ～非同期のお作法～](https://qiita.com/namn1125/items/c840d48f8f558613ec10)
- [【36】 ブロッキング・非同期用の実装・キャンセル ～ラストスパート！～](https://qiita.com/namn1125/items/39c139da3f1f26264353)
- [【37】 Axumでクラサバ！ ～最終回～](https://qiita.com/namn1125/items/bae492f823fbe60e9974)
- [【おまけ1】 Rustで勘違いしていたこと3選 🏄🌴 【100 Exercises To Learn Rust 🦀 完走記事 🏃】](https://qiita.com/namn1125/items/2a59f486cce30fd2b889)
- [【おまけ2】 【🙇 懺悔 🙇】Qiitanグッズ欲しさに1日に33記事投稿した話またはQiita CLIとcargo scriptを布教する的な何か](https://qiita.com/namn1125/items/8e552694b389c5b13b5e)
</details>

[100 Exercise To Learn Rust](https://rust-exercises.com/) 演習第8回になります！3章の終わりと4章の最初が混在しています。

今回の関連ページ

- [3.11. Destructors](https://rust-exercises.com/03_ticket_v1/11_destructor)
- [3.12. Outro](https://rust-exercises.com/03_ticket_v1/12_outro)
- [4.1. Trait](https://rust-exercises.com/04_traits/01_trait)

# [[03_ticket_v1/11_destructor](https://github.com/mainmatter/100-exercises-to-learn-rust/blob/main/exercises/03_ticket_v1/11_destructor/src/lib.rs)] デストラクタ (ちょっとライフタイム要素)

- [3.11. Destructors](https://rust-exercises.com/03_ticket_v1/11_destructor)

このエクササイズには問題はありません。「 `Drop` トレイト等を説明するまでは適切なエクササイズはできない」とメモ書きがしてあり、よって解く要素はないです。トレイトに関しては本記事より新しく入る4章にて取り上げられています。

<details><summary>一応どんな内容が書いてるか</summary>

```rust:lib.rs
// We need some more machinery to write a proper exercise for destructors.
// We'll pick the concept up again in a later chapter after covering traits and
// interior mutability.
fn outro() -> &'static str {
    "I have a basic understanding of __!"
}

#[cfg(test)]
mod tests {
    use crate::outro;

    #[test]
    fn test_outro() {
        assert_eq!(outro(), "I have a basic understanding of destructors!");
    }
}
```
</details>

というわけで解説項では本エクササイズの[Book](https://rust-exercises.com/03_ticket_v1/11_destructor)を補足する形として、C言語のソースコードでも載せてみたいと思います。

Rustのドロップ( `drop` )は、(ヒープにあるリソースに対しては)ずばり `free` 関数に相当します(マサカリ飛んできそう)。

```c:main.c
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    int *p;

    p = (int*)malloc(sizeof(int) * 16);

    p[0] = 10;

    printf("%d\n", *p);

    free(p); // リソースの片付け // drop相当！
    // free(p); <- 二重解放！

    // printf("%d\n", *p); <- 無効なポインタの参照！

    return 0;
}
```

C言語で言う `malloc` 関数でヒープに確保したメモリは、ポインタさえあればいつでもアクセスを試みることができてしまいます(そしてランタイムでアクセスできないことが発覚します)。そして要らなくなったら領域を解放する責任があります。

わざわざ言うまでもないかもしれませんが、言語によってこのヒープメモリ管理方法は2つの派閥があるわけです。

- C/C++: 自分で管理する[^free]。言わずがな二重freeやダングリングポインタ等様々なバグを生んできた
- ガベコレ言語(Javaとか): ガベコレランタイム等が使わなくなったメモリであるかを判断して自動的に解放してくれる

Rustは第3の選択肢 **「基本的[^rc]にはスコープが外れたら片付ける」** を提供してくれています。(`Rc`もガベコレと呼ばれる辺りの関係で、)個人的にはRustは広義のガベコレ言語に含まれる気がするのですが、**メモリやリソースが解放されるタイミングを所有権・ライフタイムやスコープという形でプログラマが意識しなければならない** という点が他言語と特に異なるので、別なパラダイムとして扱われている気がします。しかし所有権やライフタイムのお陰で **コンパイル時に** 二重解放やダングリングポインタに相当するバグを潰すことができるわけです。Rustが堅牢な言語と呼ばれる所以があります。

[^rc]: 例外が `Rc` 等です。しかし例外的にすぐに解放されるわけではないことが型を通してソースコードからわかる点が、他言語より有利に働いている点と見れます。

[^free]: よく「 `free` 関数は絶対呼ばなければダメか？」という議論が出ます。100 Exercisesでも後ほど似たようなシチュエーションになり言及があった気がしますが(確か [第28回](https://qiita.com/namn1125/items/34a0aacd4d40f9234aef) ？要はRust語で言えば「リークさせて良いか？」とほぼ同じ意味ということ)、筆者は「メモリリークが降り積もりリソースを無限に食べてしまうような **常駐** プログラム、ではないならば律儀に呼ぶ必要はない」というスタンスです。最も、(C言語をこの先書くことはほとんど無いとは思いますが)書いたコードがいつ常駐プログラムに組み込まれるかわかりませんから、やっぱり書いたほうが行儀が良いという結論には変わりませんね。Rustは自動的にこの行儀を守っていると見ることもできそうです。

ところで「スコープを抜け出したらDrop」という挙動はメモリだけの話にとどまらないリソース管理の考え **[RAII](https://ja.wikipedia.org/wiki/RAII)** を理解しやすいものにしてくれていることの恩恵の方が大きいと筆者は思っています。例えば、 [`std::fs::File::open`](https://doc.rust-lang.org/std/fs/struct.File.html) でオープンしたファイルは、Rustの場合 `close` のようなメソッドでクローズしなくても、ファイルハンドラがドロップされた時点で自動的に閉じてくれます。この辺の話は、冒頭でも話した通り `Drop` トレイトが関係する話なので、 [第12回](https://qiita.com/namn1125/items/c6f34945f331c7229746) でもう一度触れたいです！

# [[03_ticket_v1/12_outro](https://github.com/mainmatter/100-exercises-to-learn-rust/blob/main/exercises/03_ticket_v1/12_outro/src/lib.rs)] 3章まとめ

- [3.12. Outro](https://rust-exercises.com/03_ticket_v1/12_outro)

本章のまとめとなる問題ですね。問題指示は次のとおりです。

- TODO: `Order` (注文)構造体を定義してください
    - `product_name`, `quantity`, `unit_price` フィールドを持ちます
    - `product_name` は1文字以上300文字以下
    - `quantity` は正の数
    - `unit_price` も正の数
    - 注文の合計金額を算出する `total` メソッドを実装してください
    - 注文のフィールドにはゲッターとセッターを設けてください
- テストは `tests` フォルダの方にまとめています(後述)
- テストから参照できるように然るべきメソッドは `pub` を付けて公開する必要があります

規模が大きいからかテストは `tests/integration.rs` にまとめられています。 `tests` は特殊なディレクトリで、テスト用のファイルを置いておける場所になっています。(他にも特殊なものとして、 `bin` ディレクトリはエントリポイントになるプログラムを置いておけたりする)

<details><summary>テスト</summary>

```rust:tests/integration.rs
use outro_02::Order;

// Files inside the `tests` directory are only compiled when you run tests.
// As a consequence, we don't need the `#[cfg(test)]` attribute for conditional compilation—it's
// implied.

#[test]
fn test_order() {
    let mut order = Order::new("Rusty Book".to_string(), 3, 2999);

    assert_eq!(order.product_name(), "Rusty Book");
    assert_eq!(order.quantity(), &3);
    assert_eq!(order.unit_price(), &2999);
    assert_eq!(order.total(), 8997);

    order.set_product_name("Rust Book".to_string());
    order.set_quantity(2);
    order.set_unit_price(3999);

    assert_eq!(order.product_name(), "Rust Book");
    assert_eq!(order.quantity(), &2);
    assert_eq!(order.unit_price(), &3999);
    assert_eq!(order.total(), 7998);
}

// Validation tests
#[test]
#[should_panic]
fn test_empty_product_name() {
    Order::new("".to_string(), 3, 2999);
}

#[test]
#[should_panic]
fn test_long_product_name() {
    Order::new("a".repeat(301), 3, 2999);
}

#[test]
#[should_panic]
fn test_zero_quantity() {
    Order::new("Rust Book".to_string(), 0, 2999);
}

#[test]
#[should_panic]
fn test_zero_unit_price() {
    Order::new("Rust Book".to_string(), 3, 0);
}
```
</details>

## 解説

```rust:lib.rs
pub struct Order {
    product_name: String,
    quantity: u32,
    unit_price: u32,
}

impl Order {
    pub fn new(product_name: String, quantity: u32, unit_price: u32) -> Self {
        validate_product_name(&product_name);
        validate_quantity(quantity);
        validate_unit_price(unit_price);

        Self {
            product_name,
            quantity,
            unit_price,
        }
    }

    pub fn product_name(&self) -> &str {
        &self.product_name
    }

    pub fn set_product_name(&mut self, product_name: String) {
        validate_product_name(&product_name);

        self.product_name = product_name;
    }

    pub fn quantity(&self) -> &u32 {
        &self.quantity
    }

    pub fn set_quantity(&mut self, quantity: u32) {
        validate_quantity(quantity);

        self.quantity = quantity;
    }

    pub fn unit_price(&self) -> &u32 {
        &self.unit_price
    }

    pub fn set_unit_price(&mut self, unit_price: u32) {
        validate_unit_price(unit_price);

        self.unit_price = unit_price;
    }

    pub fn total(&self) -> u32 {
        self.quantity * self.unit_price
    }
}

fn validate_product_name(product_name: &str) {
    assert!(!product_name.is_empty());
    assert!(product_name.len() <= 300);
}

fn validate_quantity(quantity: u32) {
    assert!(quantity > 0);
}

fn validate_unit_price(unit_price: u32) {
    assert!(unit_price > 0);
}
```

復習問題だからあまり解説することがない...Rustでプログラミングするとして上記のコードはほぼ違和感がない仕上がりになっていると自負していますが、やはりバリデーションがパニックするのが違和感ですね...早く `Result` 型を使用したいです。

# [[04_traits/01_trait](https://github.com/mainmatter/100-exercises-to-learn-rust/blob/main/exercises/04_traits/01_trait/src/lib.rs)] トレイト

- [4.1. Trait](https://rust-exercises.com/04_traits/01_trait)

問題はこちらです。短いのでテストも載せてしまいます。

```rust:lib.rs
// Define a trait named `IsEven` that has a method `is_even` that returns a `true` if `self` is
// even, otherwise `false`.
//
// Then implement the trait for `u32` and `i32`.

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_u32_is_even() {
        assert!(42u32.is_even());
        assert!(!43u32.is_even());
    }

    #[test]
    fn test_i32_is_even() {
        assert!(42i32.is_even());
        assert!(!43i32.is_even());
        assert!(0i32.is_even());
        assert!(!(-1i32).is_even());
    }
}
```

トレイト `IsEven` を定義して `u32` 型の変数と `i32` 型の変数に実装してくださいという問題です。

トレイトは他言語でいうインターフェースのようなもので、今後様々な場所で登場します。Rustを理解する上で避けては通れず、あるいはRustのヘンテコな機能なんかは大体型とこのトレイトで説明されていることが多いです。

## 解説

複数通り解法が思いつきますが、とりあえず一番シンプルそうな素直に実装する方法を取りました。

```rust:lib.rs
trait IsEven {
    fn is_even(&self) -> bool;
}

impl IsEven for u32 {
    fn is_even(&self) -> bool {
        *self % 2 == 0
    }
}

impl IsEven for i32 {
    fn is_even(&self) -> bool {
        *self % 2 == 0
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_u32_is_even() {
        assert!(42u32.is_even());
        assert!(!43u32.is_even());
    }

    #[test]
    fn test_i32_is_even() {
        assert!(42i32.is_even());
        assert!(!43i32.is_even());
        assert!(0i32.is_even());
        assert!(!(-1i32).is_even());
    }
}
```

特に解説できることがない... `trait IsEven {...}` でトレイトを定義して、 `impl IsEven for u32 {...}` 等で具体的な実装を与えています。

トレイトに出会った時に特徴的に感じるのは、メソッドの引数に `self` を使えることでしょうか？ `self` の型はトレイト定義時点では決まっていないのに、メソッドの引数に取れるというのはなんとも奇妙な感じがしますが、この後出てくるジェネリクスや関連型の存在を鑑みると別におかしくないのかもしれません。

ちなみに別な解法としては、ボイラーテンプレート削減のために [`num::Num`](https://docs.rs/num/latest/num/trait.Num.html) トレイトを使うか、 マクロを使う方法がありますが、後々登場しそうだったので今回は控えました。

では次の問題に行きましょう！

次の記事: [【9】 Orphan rule (孤児ルール)・演算子オーバーロード・derive ～Empowerment \U0001F4AA ～](https://qiita.com/namn1125/items/e565713b423301a80c76)