m03-mutability について
データを可変にする必要性と方法を判断する Rust プログラミングスキルです。借用規則を説明し、スレッド環境やデータ型に応じて内部可変性の手段を比較します。
主な機能
- 01可変性の必要性を評価
- 02内部可変性パターンを比較
- 03借用規則を適用
- 04スレッドセーフな変更方法を選択
活用シーン
- 01設計変更で借用競合を解消
- 02単一スレッドコードで Cell または RefCell を選択
- 03並行データに Mutex、RwLock、または atomic を選択
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ actionbook/ rust-skills --skill m03-mutabilityインストール後、Claude Code で/m03-mutabilityを入力して読み込まれたか確認します。
詳細
可変性
レイヤー 1:言語の仕組み
核となる問い
このデータを変更する必要があるのはなぜで、誰が変更できるのでしょうか?
内部可変性を追加する前に、次の点を理解してください。
- 変更は不可欠ですか、それとも偶発的な複雑さですか?
- 誰が変更を制御すべきですか?
- 変更のパターンは安全ですか?
エラー → 設計上の問い
| エラー | 単にこう言わない | 代わりにこう尋ねる |
|---|---|---|
| E0596 | 「mut を追加する」 | 本当に可変にする必要がありますか? |
| E0499 | 「借用を分割する」 | データ構造は適切ですか? |
| E0502 | 「スコープを分ける」 | なぜ両方の借用が必要ですか? |
| RefCell panic | 「try_borrow を使う」 | 実行時チェックは適切ですか? |
考えるためのプロンプト
可変性を追加する前に、次の点を検討してください。
-
変更は必要ですか?
- 変換する → 新しい値を返す方法もあります
- ビルダーを使う → 不変のまま構築する方法もあります
-
誰が変更を制御しますか?
- 外部の呼び出し元 →
&mut T - 内部ロジック → 内部可変性
- 並行アクセス → 同期化された可変性
- 外部の呼び出し元 →
-
スレッドの状況はどうなっていますか?
- 単一スレッド → Cell/RefCell
- マルチスレッド → Mutex/RwLock/Atomic
上へたどる ↑
可変性の競合が解消されない場合:
E0499/E0502 (borrow conflicts)
↑ Ask: Is the data structure designed correctly?
↑ Check: m09-domain (should data be split?)
↑ Check: m07-concurrency (is async involved?)
| 継続するエラー | たどる先 | 問い |
|---|---|---|
| 繰り返し発生する借用の競合 | m09-domain | データを再構成すべきですか? |
| async 内の RefCell | m07-concurrency | Send/Sync が必要ですか? |
| Mutex のデッドロック | m07-concurrency | ロックの設計は適切ですか? |
下へたどる ↓
設計から実装へ:
"Need mutable access from &self"
↓ T: Copy → Cell<T>
↓ T: !Copy → RefCell<T>
"Need thread-safe mutation"
↓ Simple counters → AtomicXxx
↓ Complex data → Mutex<T> or RwLock<T>
"Need shared mutable state"
↓ Single-thread: Rc<RefCell<T>>
↓ Multi-thread: Arc<Mutex<T>>
借用ルール
At any time, you can have EITHER:
├─ Multiple &T (immutable borrows)
└─ OR one &mut T (mutable borrow)
Never both simultaneously.
クイックリファレンス
| パターン | スレッドセーフ | 実行時コスト | 使用する場面 |
|---|---|---|---|
&mut T | 該当なし | ゼロ | 排他的な可変アクセス |
Cell<T> | いいえ | ゼロ | 参照が不要な Copy 型 |
RefCell<T> | いいえ | 実行時チェック | Copy ではなく、実行時の借用が必要な場合 |
Mutex<T> | はい | ロック競合 | スレッドセーフな変更 |
RwLock<T> | はい | ロック競合 | 読み取りが多く、書き込みが少ない場合 |
Atomic* | はい | 最小限 | 単純な型(bool、usize) |
エラーコードリファレンス
| エラー | 原因 | 簡単な修正方法 |
|---|---|---|
| E0596 | 不変として借用した値を可変として扱っている | mut を追加するか、設計を見直す |
| E0499 | 複数の可変借用 | コードの流れを再構成する |
| E0502 | & が存在する間に &mut を使っている | 借用のスコープを分ける |
内部可変性の選択
| 状況 | 選択肢 |
|---|---|
| T: Copy、単一スレッド | Cell<T> |
| T: !Copy、単一スレッド | RefCell<T> |
| T: Copy、マルチスレッド | AtomicXxx |
| T: !Copy、マルチスレッド | Mutex<T> または RwLock<T> |
| 読み取りが多い、マルチスレッド | RwLock<T> |
| 単純なフラグ/カウンター | AtomicBool、AtomicUsize |
アンチパターン
| アンチパターン | 問題点 | より良い方法 |
|---|---|---|
| あらゆる場所で RefCell を使う | 実行時パニック | 明確な所有権設計 |
| 単一スレッドで Mutex を使う | 不要なオーバーヘッド | RefCell |
| RefCell のパニックを無視する | デバッグが困難 | 対処するか、構成を見直す |
| ホットループ内でロックする | パフォーマンスを大きく損なう | 操作をまとめて実行する |
関連スキル
| 場面 | 参照先 |
|---|---|
| スマートポインターの選択 | m02-resource |
| スレッドセーフティ | m07-concurrency |
| データ構造の設計 | m09-domain |
| アンチパターン | m15-anti-pattern |
コメント
Markdown 対応