Rust 100 Ex 🏃【28/37】 リーク・スコープ付きスレッド ～ライフタイムに技あり！～

前の記事

- [【0】 準備](https://qiita.com/namn1125/items/bd1b4cd028874189a3c1) ← 初回
- ...
- [【27】 スレッド・'staticライフタイム ～並列処理に見るRustの恩恵～](https://qiita.com/namn1125/items/5a4a09172834063268b1) ← 前回
- 【28】 リーク・スコープ付きスレッド ～ライフタイムに技あり！～ ← 今回

<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/) 演習第28回になります！

今回の関連ページ

- [7.3. Leaking memory](https://rust-exercises.com/07_threads/03_leak)
- [7.4. Scoped threads](https://rust-exercises.com/07_threads/04_scoped_threads)

# [[07_threads/03_leak](https://github.com/mainmatter/100-exercises-to-learn-rust/blob/main/exercises/07_threads/03_leak/src/lib.rs)] 意図的なメモリリーク

- [7.3. Leaking memory](https://rust-exercises.com/07_threads/03_leak)

問題はこちらです。同じネタが続いてます。

```rust:lib.rs
// TODO: Given a vector of integers, leak its heap allocation.
//  Then split the resulting static slice into two halves and
//  sum each half in a separate thread.
//  Hint: check out `Vec::leak`.

use std::thread;

pub fn sum(v: Vec<i32>) -> i32 {
    todo!()
}
```

TODOを要約すると以下のような感じです。

- 整数のベクトルが与えられるので、そのヒープアロケーションをメモリリークさせちゃってください！
- そうすれば `&'static` 参照を得られるので、ここまでの演習通り足し合わせてください

<details><summary>テストを含めた全体</summary>

```rust:lib.rs
// TODO: Given a vector of integers, leak its heap allocation.
//  Then split the resulting static slice into two halves and
//  sum each half in a separate thread.
//  Hint: check out `Vec::leak`.

use std::thread;

pub fn sum(v: Vec<i32>) -> i32 {
    todo!()
}

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

    #[test]
    fn empty() {
        assert_eq!(sum(vec![]), 0);
    }

    #[test]
    fn one() {
        assert_eq!(sum(vec![1]), 1);
    }

    #[test]
    fn five() {
        assert_eq!(sum(vec![1, 2, 3, 4, 5]), 15);
    }

    #[test]
    fn nine() {
        assert_eq!(sum(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]), 45);
    }

    #[test]
    fn ten() {
        assert_eq!(sum(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), 55);
    }
}
```
</details>

## 解説

[`leak`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.leak) メソッドを使うことで、 `&'static mut [T]` を引き出せるので、これを使って後は前回と同様の処理です！

```rust:lib.rs
use std::thread;

pub fn sum(v: Vec<i32>) -> i32 {
    let slice: &'static mut [i32] = v.leak();

    let (s1, s2) = slice.split_at(slice.len() / 2);

    vec![s1, s2]
        .into_iter()
        .map(|s| thread::spawn(move || s.iter().sum::<i32>()))
        .collect::<Vec<_>>()
        .into_iter()
        .map(|handle| handle.join().unwrap())
        .sum()
}
```

仕組みとしては、ベクトルが参照しているヒープ先を所有権の仕組みから外し、ベクトルのライフタイムが尽きた後でも片付けられないようにしています。つまり、プログラム終了までメモリが有効に残り続ける「メモリリーク」を意図的に起こさせています！

```mermaid
sequenceDiagram
    participant Main as mainスレッド
    participant Static as 静的データ
    participant T1 as 別スレッド
    Note over Main: v: Vec<i32>を作成
    activate Main
    Main->>Static: vをリーク！
    activate Static
    deactivate Main
    Static->>T1: &'static mut [i32] としてvの中身を借りる
    activate T1
    Note right of T1: 参照だけど、staticだから安心！
    Note over Static: 全プログラム終了時に片付け
    deactivate T1
    deactivate Static
```

「メモリリーク？！なんだか危なそう...」と思った方、メモリリークは"基本的には"全く問題なく安全です。なぜなら、[Book](https://rust-exercises.com/07_threads/03_leak)にも書いてある通り、「リークさせたメモリもプロセス終了後にOSが勝手に解放してくれるから」です。

メモリリークが問題になるとして、考えられるのは次の2点ぐらいです。

- **常駐**アプリケーション(Webサーバーとか)を動かす場合で、リクエスト毎などに確保されたリソースをリークしてしまう場合
    - メモリリークが問題になる典型例です
- 手動実装の `Drop` トレイト処理を動かさなければならない場合
    - `drop` メソッドが呼ばれなくなります。(おそらく、呼ぶとしてどの順番で何を呼べば良いの？という問題があるため)
    - Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=d3665b648c9d09ad643221e1b4a6acf0

[第8回](https://qiita.com/namn1125/items/7247edde3b6bd0b85b25)の注釈で `free` を呼ぶべきかみたいな話を地味にしていたのですが、筆者としてはすぐ終わるようなタイプの処理なら全然書いてもいいんじゃないかと思っています。

まぁ今回の演習は「リークさせる手もあるよ！」ということの紹介程度の内容でしょう。やって良いとは言え、明示的にリークさせることは少なく[^leak]、大体は最初のほうで取り組んだ通りクローンしてしまったり、以降で紹介されるようなパターンや手法を使うことの方が多いと思います。

[^leak]: 例外的に使用することがあるとすると例えば [FFI](https://doc.rust-jp.rs/rust-by-example-ja/std_misc/ffi.html) が絡む時が挙げられます。Rustの所有権の枠組みだと不都合がある時にしばしば [`std::mem::forget`](https://doc.rust-lang.org/std/mem/fn.forget.html) を呼んだりします。

# [[07_threads/04_scoped_threads](https://github.com/mainmatter/100-exercises-to-learn-rust/blob/main/exercises/07_threads/04_scoped_threads/src/lib.rs)] スコープ付きスレッド

- [7.4. Scoped threads](https://rust-exercises.com/07_threads/04_scoped_threads)

問題はこちらです。

```rust:lib.rs
// TODO: Given a vector of integers, split it in two halves
//  and compute the sum of each half in a separate thread.
//  Don't perform any heap allocation. Don't leak any memory.

pub fn sum(v: Vec<i32>) -> i32 {
    todo!()
}
```

`&'static [i32]` がやってきてくれるわけではなく最初の問題と同様に `Vec<i32>` がやってきています。その上で

- 新しいヒープを確保するな！
- メモリリークを使うな！

という条件が足され、本エクササイズテーマのスコープ付きスレッドを使わせたいという感じの問題になっています！

<details><summary>テストを含めた全体</summary>

```rust:lib.rs
// TODO: Given a vector of integers, split it in two halves
//  and compute the sum of each half in a separate thread.
//  Don't perform any heap allocation. Don't leak any memory.

pub fn sum(v: Vec<i32>) -> i32 {
    todo!()
}

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

    #[test]
    fn empty() {
        assert_eq!(sum(vec![]), 0);
    }

    #[test]
    fn one() {
        assert_eq!(sum(vec![1]), 1);
    }

    #[test]
    fn five() {
        assert_eq!(sum(vec![1, 2, 3, 4, 5]), 15);
    }

    #[test]
    fn nine() {
        assert_eq!(sum(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]), 45);
    }

    #[test]
    fn ten() {
        assert_eq!(sum(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), 55);
    }
}
```
</details>

## 解説

スレッド付きスコープは、「**スレッドがいつまで生きているかわからないから**」 `'static` ライフタイムにする必要があったなら、「スレッドの生存範囲をそもそも狭めてやればよい」という解決策です！

```rust:lib.rs
use std::thread;

pub fn sum(v: Vec<i32>) -> i32 {
    let mid = v.len() / 2;

    thread::scope(|scope| {
        // (&v[..mid]).into_iter()...とする必要はないらしい
        let h1 = scope.spawn(|| v[..mid].into_iter().sum::<i32>());
        let h2 = scope.spawn(|| v[mid..].into_iter().sum::<i32>());

        h1.join().unwrap() + h2.join().unwrap()
    })
}
```

`scope.spawn` で生成されたスレッドは `v` のライフタイムより短いライフタイムであることが確約されているため、 `v` の参照を渡すことができるようになっています。スコープ付きならば、今まではできなかった「別スレッドへ `'static` じゃない参照を渡す」ということが可能になるわけです！

```mermaid
sequenceDiagram
    participant Main as mainスレッド
    Note over Main: v: Vec<i32>を作成
    activate Main
    Note right of Main: scopeを作成
    create participant T1 as スコープ付きスレッド
    Main->T1: スコープ付きスレッドを作成
    Main->>T1: &vとして借用
    activate T1
    Note right of T1: &vを使って計算
    deactivate T1
    destroy T1
    Main->T1: スコープ付きスレッド終了
    Note right of Main: scopeを破棄
    Note over Main: vを破棄
    deactivate Main
```

ところで、スレッド付きスレッドは `scope` というローカル変数を用いて生成されている部分が興味深いですね。もしかしたら `thread::scope(|scope| {...})` の中で `thread::spawn` を呼び出す感じの仕組みにしようと思えばできたのかもしれませんが、あえて `scope` という変数を導入することで、**この変数のライフタイムより長いライフタイムを持つ変数ならスレッド内で扱える** というわかりやすい判断基準が導入されています！要は読みやすいのです。Rustのライフタイムに慣れている人にとって親切な設計となっており、ライフタイムを上手く活用しているのが面白いなと感じました。

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

次の記事: [【29】 チャネル・参照の内部可変性 ～Rustの虎の子、mpscと `Rc<RefCell<T>>`～](https://qiita.com/namn1125/items/861e72dbb4b837508948)

<details><summary>登場したPlayground</summary>

(実際に無効化したことはないですが、)Rust Playground上のデータが喪失する可能性を鑑みて、一応記事にもソースコードを掲載することとしました。

URL: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=d3665b648c9d09ad643221e1b4a6acf0

```rust:Rust
struct Hoge(i32);

impl Drop for Hoge {
    fn drop(&mut self) {
        println!("Drop!: {}", self.0);
    }
}

fn main() {
    let v = vec![Hoge(1), Hoge(2), Hoge(3)];
    
    v.leak(); // この行の有無で `drop` が呼ばれるか呼ばれないか挙動が変化
}
```
</details>