Rust 100 Ex 🏃【33/37】 チャネルなしで実装・Syncの話 ～考察回です～

前の記事

- [【0】 準備](https://qiita.com/namn1125/items/bd1b4cd028874189a3c1) ← 初回
- ...
- [【32】 `Send`・排他的ロック(`Mutex`)・非対称排他的ロック(`RwLock`) ～真打 `Arc<Mutex<T>>` 登場～](https://qiita.com/namn1125/items/eee890dc8672e975e70c) ← 前回
- 【33】 チャネルなしで実装・Syncの話 ～考察回です～ ← 今回

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

今回の関連ページ

- [7.13. Without channels](https://rust-exercises.com/07_threads/13_without_channels)
- [7.14. Sync trait](https://rust-exercises.com/07_threads/14_sync)

# [[07_threads/13_without_channels](https://github.com/mainmatter/100-exercises-to-learn-rust/blob/main/exercises/07_threads/13_without_channels/src/lib.rs)] `Arc<RwLock<T>>` だけで実装！

- [7.13. Without channels](https://rust-exercises.com/07_threads/13_without_channels)

本問題は今までのサーバークライアントシステムを全部なかったことにし(！)、 `TicketStore` も `Arc<RwLock<T>>` で包んでしまおう！という回です。ほぼ何もする必要がありません！

```rust:tests/check.rs
// ...超省略！！...
use std::sync::{Arc, RwLock};

#[test]
fn works() {
    let store = todo!();

    // ...全部省略！...
}

```

<details><summary>一応全体(回答済み)</summary>

```rust:src/lib.rs
// TODO: You don't actually have to change anything in the library itself!
//  We mostly had to **remove** code (the client type, the launch function, the command enum)
//  that's no longer necessary.
//  Fix the `todo!()` in the testing code and see how the new design can be used.

pub mod data;
pub mod store;
```

```rust:src/data.rs
use crate::store::TicketId;
use ticket_fields::{TicketDescription, TicketTitle};

#[derive(Clone, Debug, PartialEq)]
pub struct Ticket {
    pub id: TicketId,
    pub title: TicketTitle,
    pub description: TicketDescription,
    pub status: Status,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TicketDraft {
    pub title: TicketTitle,
    pub description: TicketDescription,
}

#[derive(Clone, Debug, Copy, PartialEq, Eq)]
pub enum Status {
    ToDo,
    InProgress,
    Done,
}
```

```rust:src/store.rs
use std::collections::BTreeMap;
use std::sync::{Arc, RwLock};

use crate::data::{Status, Ticket, TicketDraft};

#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct TicketId(u64);

#[derive(Clone)]
pub struct TicketStore {
    tickets: BTreeMap<TicketId, Arc<RwLock<Ticket>>>,
    counter: u64,
}

impl TicketStore {
    pub fn new() -> Self {
        Self {
            tickets: BTreeMap::new(),
            counter: 0,
        }
    }

    pub fn add_ticket(&mut self, ticket: TicketDraft) -> TicketId {
        let id = TicketId(self.counter);
        self.counter += 1;
        let ticket = Ticket {
            id,
            title: ticket.title,
            description: ticket.description,
            status: Status::ToDo,
        };
        let ticket = Arc::new(RwLock::new(ticket));
        self.tickets.insert(id, ticket);
        id
    }

    pub fn get(&self, id: TicketId) -> Option<Arc<RwLock<Ticket>>> {
        self.tickets.get(&id).cloned()
    }
}
```

```rust:tests/check.rs
use std::sync::{Arc, RwLock};
use std::thread::spawn;

use ticket_fields::test_helpers::{ticket_description, ticket_title};
use without_channels::data::TicketDraft;
use without_channels::store::TicketStore;

#[test]
fn works() {
    let store = Arc::new(RwLock::new(TicketStore::new()));

    let store1 = store.clone();
    let client1 = spawn(move || {
        let draft = TicketDraft {
            title: ticket_title(),
            description: ticket_description(),
        };
        store1.write().unwrap().add_ticket(draft)
    });

    let store2 = store.clone();
    let client2 = spawn(move || {
        let draft = TicketDraft {
            title: ticket_title(),
            description: ticket_description(),
        };
        store2.write().unwrap().add_ticket(draft)
    });

    let ticket_id1 = client1.join().unwrap();
    let ticket_id2 = client2.join().unwrap();

    let reader = store.read().unwrap();

    let ticket1 = reader.get(ticket_id1).unwrap();
    assert_eq!(ticket_id1, ticket1.read().unwrap().id);

    let ticket2 = reader.get(ticket_id2).unwrap();
    assert_eq!(ticket_id2, ticket2.read().unwrap().id);

    // Patchパターン (storeは読み取りモードで、個別のチケットだけ修飾する場合) は確かめていないらしい...
}
```
</details>

## 解説

変更箇所は `TicketStore` も最初に `Arc::new(Mutex::new(...))` で包むだけになります！

```rust:lib.rs
#[test]
fn works() {
    let store = Arc::new(RwLock::new(TicketStore::new()));
}
```

図で表すと次のようになった形です、サーバーを介す必要がなくなり、スッキリしました！(大嘘)

**元々の構造 (前回最後)**

```mermaid
sequenceDiagram
    actor C1 as クライアント1
    actor C2 as クライアント2
    participant T1 as チケット id: 1
    actor S  as サーバー
    actor C3 as クライアント3
    C1->>S: Get { id: 1 }
    S->>C1: チケット id: 1 への Arc
    C2->>S: Get { id: 1 }
    S->>C2: チケット id: 1 への Arc
    C3->>S: Get { id: 1 }
    S->>C3: チケット id: 1 への Arc
    C1->>T1: 読み取りロック獲得
    activate T1
    C2->>T1: 読み取りロック獲得
    Note over T1: クライアント1, 2がReadロック中
    C3-xT1: ❌️読み書きロック獲得できず！
    activate C3
    Note over C3: 読み書きロック獲得できるまで待機
    C1->>T1: ロック解除！
    C2->>T1: ロック解除！
    deactivate T1
    C3->>T1: 読み書きロック獲得
    deactivate C3
    activate T1
    Note over T1: クライアント3がロック中
    C1-xT1: ❌️読み取りロック獲得できず！
    C3->>T1: ロック解除！
    deactivate T1
```

**もはや必要なのはクライアント(アクセスを行うスレッド)のみ**

[RW] : 読み書き専有ロック
[RO] : 読み取り共用ロック

```mermaid
sequenceDiagram
    actor C1 as クライアント1
    actor C2 as クライアント2
    actor C3 as クライアント3
    participant TS as チケットストア
    C1->>TS: [RW] ロック獲得
    activate TS
    Note right of TS: [RW] by クライアント1
    C2-xTS: ❌️ [RO] ロック獲得できず
    activate C2
    C1->>TS: add_ticket id: 1
    create participant T1 as チケット id: 1
    TS->>T1: 作成
    C1->>TS: ロック解除！
    deactivate TS
    C2->>TS: [RO]
    deactivate C2
    activate TS
    Note right of TS: [RO]
    C2->>TS: get id: 1
    TS->>C2: Arc<Mutex<Ticket id: 1>>
    C1-xTS: ❌️ [RW] ロック獲得できず
    C3->>TS: get id: 1
    TS->>C3: Arc<Mutex<Ticket id: 1>>
    C2->>T1: [RW] ロック獲得
    activate T1
    Note right of T1: [RW] by クライアント2
    C3-xT1: ❌️ [RO] ロック獲得できず
    activate C3
    C2->>T1: ロック解除！
    deactivate T1
    C2->>TS: ロック解除！
    C3->>T1: [RO] ロック獲得
    deactivate C3
    activate T1
    Note right of T1: [RO]
    C3->>T1: ロック解除！
    deactivate T1
    C3->>TS: ロック解除！
    deactivate TS
```

例の網羅性のために処理内容は変えていますが...うーん、カオス！

[Book](https://rust-exercises.com/100-exercises/07_threads/13_without_channels) には「最もきめ細かい並行処理」みたいな感じで紹介されておりますが、「この方針が一番良い！」とはあまり思えないですね...実際、ストアのロックとチケットのロックは独立しており、ストアのロックを得たからといってチケットの変更操作までロックできていないという点が長所短所両方を抱えています。

```mermaid
sequenceDiagram
    actor C1 as クライアント1
    actor C2 as クライアント2
    participant TS as チケットストア
    C1->>TS: [RW] ロック獲得
    activate TS
    Note right of TS: [RW] by クライアント1
    create participant T1 as チケット id: 1
    TS->>T1: 作成
    C1->>TS: add_ticket id: 1 & ロック解除！
    deactivate TS
    C2->>TS: [RO] ロック獲得
    activate TS
    Note right of TS: [RO]
    C2->>TS: get id: 1
    TS->>C2: Arc<Mutex<Ticket id: 1>>
    C2->>T1: [RO] ロック獲得
    activate T1
    Note right of T1: [RO]
    Note right of T1: ストアのロックとは独立！
    C2->>TS: ロック解除！
    deactivate TS
    C1->>TS: [RW] ロック獲得
    activate TS
    Note right of TS: [RW] by クライアント1
    C1->>TS: add_ticket id: 2 & ロック解除！
    deactivate TS
    C2->>T1: ロック解除！
    deactivate T1
```

<details><summary>PoCソースコード</summary>

```rust:tests/check.rs (追記)
// 上部省略

use std::sync::mpsc::{channel, Sender};

fn add_dummy_ticket(store: &mut TicketStore) -> TicketId {
    let draft = TicketDraft {
        title: ticket_title(),
        description: ticket_description(),
    };
    store.add_ticket(draft)
}

#[test]
fn ticket_op_in_store_locked() {
    let store = Arc::new(RwLock::new(TicketStore::new()));

    let (id_tx_12, id_rx_12): (Sender<TicketId>, _) = channel();
    let (tx_21, rx_21): (Sender<()>, _) = channel();

    let store1 = store.clone();
    let client1 = spawn(move || {
        let mut writer = store1.write().unwrap();
        let id1 = add_dummy_ticket(&mut writer);

        id_tx_12.send(id1).unwrap();

        drop(writer);

        rx_21.recv().unwrap();

        let mut writer = store1.write().unwrap();
        let id2 = add_dummy_ticket(&mut writer);

        id_tx_12.send(id2).unwrap();

        let _ = add_dummy_ticket(&mut writer);
    });

    let store2 = store.clone();
    let client2 = spawn(move || {
        let id1 = id_rx_12.recv().unwrap();

        let store_reader = store2.read().unwrap();

        let ticket1 = store_reader.get(id1).unwrap();

        let t1_reader = ticket1.read().unwrap();

        tx_21.send(()).unwrap();

        drop(store_reader);

        let _ = id_rx_12.recv().unwrap();

        println!("{:?}", *t1_reader);
    });

    client1.join().unwrap();
    client2.join().unwrap();
}
```
</details>

追加機能しかなく、削除機能がなければこの構造に特に問題はありません。しかし、削除できるようにするとどうでしょうか...？ `Arc` は参照カウントが0になるまでデータを保持する構造体です。編集中のストアから対象チケットの `Arc` が削除されていても、クライアントは気づくことができません！自身が持つロックを解除し、 `Arc` の所有権を失った瞬間にアクセスできなくなります。あるいは削除を実行した他のクライアントから見ると、削除したはずなのに削除したという情報がまだロックを保有しているクライアントに渡らないという事態になります。

このように複雑にしてしまうぐらいなら、各チケットにまで `Arc<RwLock<Ticket>>` を設けるのではなく、ストアのみ `Arc<Mutex<TicketStore>>` として排他的にしたほうがデータ不整合の危険性は少ないですし、そもそも最初のようにサーバーで中央集権的に扱うほうがオプションが多そうです。

確かに並行処理の効率化という観点では今回の改良は良さそうですが、実際にプログラムを組む際はあまり取らない方針なんじゃないかと思いました。

# [[07_threads/14_sync](https://github.com/mainmatter/100-exercises-to-learn-rust/blob/main/exercises/07_threads/14_sync/src/lib.rs)] SyncトレイトとSendトレイト

- [7.14. Sync trait](https://rust-exercises.com/07_threads/14_sync)

`Send` に加えてここまで解説していなかった `Sync` の話題です！本エクササイズには問題はありません。というわけでBookの内容を参照しつつ軽い検証でも行おうと思います。

## 解説

`Send` は本記事群ではまともに説明してないかもしれないですが、これ以上はよいでしょう。スレッド間でその値を転送できることを示すマーカートレイトです。

`Sync` についてです。なぜ `Sync` は `Sync` という名前であり何に必要なのか...は何か深そうなので後で考えるとして、事実ベースで考えてみたいと思います。[前回](https://qiita.com/namn1125/items/eee890dc8672e975e70c)話した通りその値の参照が `Send` ならば `Sync` ...つまり `impl Send for &T` と `impl Sync for T` が同値です。

とりあえず、事実ベースで `Send` と `Sync` がついているかどうかを確認してみます。 `RefCell` と `MutexGuard` に注目すると面白いのでした。[Book](https://rust-exercises.com/100-exercises/07_threads/14_sync) に記載されている、それぞれの理由を見てみます！

### `RefCell`

| トレイト | 可否  | 理由                                                                                                                 |
| :------- | :---: | :------------------------------------------------------------------------------------------------------------------- |
| Send     |  :o:  | `RefCell` それ自体がスレッドを跨ぐ際は借用等がされていない状態であり、跨ぐ上で障害となる存在がない。 `Send` である。 |
| Sync     |  :x:  | 実体の共有参照 `&RefCell` には `borrow_mut` 能力(参照カウンターへのアクセス権)がある。共有参照ということは `borrow_mut` 能力が**複数存在できる** 。 カウンターへアクセスする権利は、(アトミックな操作なしでは)スレッドを跨げない。以上より `&RefCell` は `!Send` であるから `RefCell` は `!Sync` |

#### `RefCell` 関連のPoCコード

1つ目 Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=33bb9c8b23cc13fa087294d217986b8d

```rust:Rust
use std::cell::RefCell;
use std::thread;

fn main() {
    let v = RefCell::new(10_usize);
    
    thread::spawn(move || {
        println!("{:?}", v.borrow());
    }).join().unwrap();
}
```

`RefCell` が `Send` であることを確認するコードです。実体がスレッドをまたいでも何も問題はなさそうですね！

2つ目 Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=4b72ed909a3d5ac2e35b1bdafdd91235

```rust:Rust (コンパイルエラー)
use std::cell::RefCell;
use std::thread;
use std::sync::Arc;

fn main() {
    let v = RefCell::new(10_usize);
    let a = Arc::new(v);
    let a2 = Arc::clone(&a);

    thread::spawn(move || {
        println!("{:?}", a2.borrow());
    }).join().unwrap();
}
```

`&RefCell` が `!Send` (つまり `RefCell` が `!Sync`) であることを確かめるコードで、せっかくなので `Arc` さんに手伝ってもらいました！ `Arc` で包まれることにより、 `&RefCell` はスレッドをまたいで存在しようとします。が！参照カウンターがレースコンディションを起こしてしまうので、本操作はダメです！それが検知されてコンパイルエラーになります。

検知の秘密は [`Arc` の `Send`](https://doc.rust-lang.org/std/sync/struct.Arc.html#impl-Send-for-Arc%3CT,+A%3E) にあります。 `Arc` は `T: Send + Sync` の時にしか `Send` になってくれません！よってこのコードの `Arc` は `!Send` となるので、コンパイルエラーになっています。

:::note 
**ちなみに`Rc`は**

`Rc` は複製を行う処理がアトミックではないため他のスレッドに移るとレースコンディションが起きてしまい `!Send` なのでした。(参照は複数あれど)実体は単一である `RefCell` と異なり、 `Rc` は複数の実体が単一の可変な値(カウンター)へ依存してしまっているので、実体ですらスレッドを跨げません！

あるいは `RefCell<T>` それ自体は `Deref<Target = T>` を実装していないから `Send` にはできる、という解釈もよさそうです。 `Deref<Target = T>` でありながらスレッドを跨ぐには、アトミックな演算が必要そうです。
:::

### `MutexGuard`

|トレイト|可否|理由|
|:-|:-:|:--|
|Send| :x: | ドロップ時に元の `Mutex` の参照カウンターを戻す責務を持っているが、スレッドを跨ぐとそれを遂行できない[^tokio_mutex]！ゆえに `!Send` である。 |
|Sync| :o: | `&MutexGuard` には **参照カウンター操作の責務がない** ため別なスレッドに送っても特に問題ない。 |

[^tokio_mutex]: [第36回](https://qiita.com/namn1125/items/39c139da3f1f26264353)でも話す予定ですが(忘れてるかも...)、非同期処理ランタイムtokio用の [`MutexGuard`](https://docs.rs/tokio/latest/tokio/sync/struct.MutexGuard.html#impl-Sync-for-MutexGuard%3C'_,+T%3E) は、スレッドをまたいでもこのカウンターを戻す能力を有しているらしく `Send` です。強いけどその分すごく重そう...

#### `MutexGuard` 関連のPoCコード

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

```rust:Rust
use std::thread;
use std::sync::{Mutex, Arc, MutexGuard};

fn thrd(amv: Arc<Mutex<String>>) {
    let mut v_mg: MutexGuard<'_, String> = amv.lock().unwrap();
    
    *v_mg = format!("{} {}", *v_mg, *v_mg);

    thread::scope(|s| {
        s.spawn(|| {
            println!("1: {}", *v_mg);
        });
        s.spawn(|| {
            println!("2: {}", *v_mg)
        });
    });

}

fn main() {
    let amv1 = Arc::new(Mutex::new("Beep".to_string()));
    let amv2 = amv1.clone();
    
    let h1 = thread::spawn(move || {thrd(amv1)});
    let h2 = thread::spawn(move || {thrd(amv2)});
    
    h1.join().unwrap();
    h2.join().unwrap();
}
```

`&MutexGuard` が `Send` (`MutexGuard` が `Sync`) なことが意外だったので、これを確認するコードです。 `'static` でない参照でもコンパイルが通るよう、 `thread::spawn` を使用しました。

あくまでも `MutexGuard` のドロップは `thrd` 関数の最後で起き、それ以外ではカウンター操作が起きないため、このようにスレッドを跨いでも問題ないことが確認できました！

### `Sync` という名称の軽い考察

最初に `Sync` の説明に出会ったとき、筆者はまだRustに慣れていなかったこともあって混乱しました。なぜなら「 `&T` が `Send` なら `T` は `Sync` 」と言われても、「`Arc` を使わないと **そもそも参照はスレッドを跨げなくないか...？**」 と思っていたからでした。実際は[第28回](https://qiita.com/namn1125/items/34a0aacd4d40f9234aef)で見た通り、 `&'static T` であったり、あるいはスコープ付きスレッド( `thread::scope` )を使えばライフタイム制限がある何某でもスレッドを跨ぐことが可能だったわけですが、当時はここまでで述べたような事実ベースでそう説明されてもよくわからなかったです。そのため、 `Sync` という名称から一生懸命に意味を考えていた時期がありました :sweat_smile:

今となっては上記で具体的な必要性を確認できたのでこれ以上悩む必要はないかもしれませんが、当時の筆者のためにも、なぜ `Sync` という名称なのか、自分が納得できる理由を考えてみたいと思います。

[TRPL](https://doc.rust-jp.rs/book-ja/ch16-04-extensible-concurrency-sync-and-send.html) の `Sync` の説明には「 `Sync` で複数のスレッドからのアクセスを許可する」「 `Sync` を実装した型は、複数のスレッドから参照されても安全であることを示唆します。」とあります。

図にしてみると...なるほど `Sync` (hronize) ですね！

```mermaid
graph LR
    val[実体]
    ref1[共有参照1]
    ref2[共有参照2]
    ref3[共有参照3]
    subgraph t1 [スレッド1]
    ref1
    end
    subgraph t2 [スレッド2]
    ref2
    end
    subgraph t3 [スレッド3]
    ref3
    end
    ref1-.->|"実体を介して\n変更がシンクロする"|ref2
    ref1-.->|"実体を介して\n変更がシンクロする"|ref3
    ref1-->val
    ref2-->val
    ref3-->val
```

共有参照は「実は不変とは限らない」という話を[第29回](https://qiita.com/namn1125/items/861e72dbb4b837508948)にて取り扱いました。ここで `RefCell` を始めとした内部可変性の話が出てきたのでした。そうすると、内部可変性を介して、「 `&` による共有参照の変更をスレッドを跨いでどう伝えるか？」という問題が出てきます。

`RefCell` は自身の共有参照間において(あるいは `Rc` 達は彼らの間において)この変更をうまく伝える (シンクロ・同期させる) 能力がなく、 `Mutex` の参照(や `Arc` )はアトミック操作のおかげで参照カウンターの変更を同期できます。ここに、 `Sync` という名称の由来がありそうですね。とりあえずこの説明で納得できそうです！

<ruby>祈手<rt>アンブラハンズ</rt></ruby> はおそらく <ruby>&黎明卿<rt>ボンドルドへの共有参照</rt></ruby> なのでしょう。そして、 <ruby>精神隷属機<rt>ゾアホリック</rt></ruby> はそれ自体は `Send` かもしれませんが、 その<ruby>参照<rt>アンブラハンズ</rt></ruby>が「層を跨ぐと都合が悪い(シンクロできない)」ことから、少なくとも `!Sync` なのだと考えられます！

## 並行処理 (複数スレッドにまたがる処理) で気を付けること まとめ

7章では並行処理及びマルチスレッド処理を見ていきました。8章ではさらに進んで非同期処理を見ていきますが、ぶっちゃけ「並行処理の勘所」さえ分かっていればそこまで難しくありません！その勘所は、まさしく並行処理文脈で出てきた `Send` ・ `Sync` ・ `'static` だと筆者は考えています。

### `'static` ライフタイム

「スレッドはいつまで存在するかわからない」ことより、「スレッドの存在期限を限定する( [`std::thread::scope`](https://doc.rust-lang.org/std/thread/fn.spawn.html) )」か、この **`'static` ライフタイムを持つ型のみやり取りする** 、という話が出てきました。簡単に言えば、困ったら `.clone()` で解決です！

### `Send`・`Sync`

「スレッドを跨げるか否か」を表すマーカートレイト群でした。 [第36回](https://qiita.com/namn1125/items/39c139da3f1f26264353)でおそらく扱う通り、スレッドを跨いだりしやすい専用の型 ( [`tokio::sync::Mutex`](https://docs.rs/tokio/latest/tokio/sync/struct.Mutex.html) など) を活用できればこれらのマーカートレイトがついてくれて解決できる場合がありますが、その際はどのみち **デッドロック** に気を付ける必要はありそうでした。非同期処理ではスレッドを跨ぎまくりますが、裏を返すとこの2つのマーカートレイトを押さえておくことがポイントでしょう。

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

次の記事: [【34】 `async fn`・非同期タスク生成 ～Rustの非同期入門～](https://qiita.com/namn1125/items/9c32b730a283ad76c891)