# Rustのスマートポインタ徹底解説: Box、Rc、Arc、RefCell(2026年版) > RustのスマートポインタBox、Rc、Arc、RefCellを、2026年のコンパイル可能な例、判断表、頻出の面接質問とともに解説します。 - Published: 2026-07-05 - Updated: 2026-07-07 - Author: SharpSkill - Tags: rust, smart-pointers, memory-management, interview, rust-2024-edition - Reading time: 11 min --- Rustのスマートポインタは、借用チェッカーだけでは表現できない所有権パターン、すなわちヒープ確保、共有所有権、そして共有参照を通じた変更を可能にするツールです。単なる参照(`&T`)が値を借用するだけなのに対し、スマートポインタは自身のデータを所有し、その上に追加の振る舞いを重ねます。本ガイドでは、Rust開発者が本番環境でも面接でも必ず出会う4つの型、`Box`、`Rc`、`Arc`、`RefCell`を、Rust 2024エディション向けのコンパイル可能な例とともに解説します。 > **1行でまとめると** > > 単一所有者によるヒープ確保には`Box`、単一スレッドでの共有所有権には`Rc`、スレッドをまたぐ共有所有権には`Arc`、共有参照を通じて値を変更するには`RefCell`を使います。`Rc>`と`Arc>`という組み合わせが、共有された可変状態をカバーします。 ## Rustにおけるスマートポインタとは スマートポインタは、ポインタのように振る舞いながら追加のメタデータや機能を備えた構造体です。その多くは`Deref`トレイトを実装しており、`*pointer`やメソッド呼び出しがあたかも通常の参照であるかのように動作します。また`Drop`トレイトも実装しているため、値がスコープを離れると後片付けが自動的に実行されます。標準ライブラリはここで扱う4つを提供しており、これらを理解する鍵は[所有権と借用](/blog/rust/ownership-borrowing-rust-complete-guide)をしっかり把握することにあります。所有権と借用が、どの確保領域をいつ誰が解放するかを決めるからです。 参照との決定的な違いは所有権です。`&T`は指し示すデータを決して所有しないため、その値より長く生きることはできません。スマートポインタはデータを所有し、そのライフタイムを制御し、決定的に解放します。[スマートポインタに関するRust Bookの章](https://doc.rust-lang.org/book/ch15-00-smart-pointers.html)がこれらを1つのカテゴリとして扱うのは、まさにこの「所有権に加えて振る舞いを持つ」という形を共有しているからです。 ## `Box`: 再帰的な型やサイズ確定のためのヒープ確保 `Box`は最もシンプルなスマートポインタです。値をヒープに格納し、スタック上にはそのポインタだけを保持します。所有者は1つだけで、確保そのもの以外にランタイムのオーバーヘッドはありません。最もよくある用途は、再帰的な型に既知のサイズを与えることです。自分自身を直接含む型は無限に大きくなってしまいますが、`Box`は単なるポインタなので、指し示す対象に関係なくサイズが固定されます。 下の例は二分木を定義しています。各`Node`は2つの子を持ちますが、間接参照がなければコンパイラは`Tree`のサイズを計算できません。各子を`Box`で包むことで、型レベルでの再帰を断ち切ります。 ```rust // tree.rs #[derive(Debug)] enum Tree { Leaf(i32), // Box puts each child on the heap, so Node has a fixed size (two pointers). Node(Box, Box), } fn sum(tree: &Tree) -> i32 { match tree { Tree::Leaf(value) => *value, // base case: return the leaf Tree::Node(left, right) => sum(left) + sum(right), // recurse into both children } } fn main() { // Build (1) + ((2) + (3)) = 6 let tree = Tree::Node( Box::new(Tree::Leaf(1)), Box::new(Tree::Node( Box::new(Tree::Leaf(2)), Box::new(Tree::Leaf(3)), )), ); println!("sum = {}", sum(&tree)); // sum = 6 } ``` `Box`は、大きな値のムーブがコストになる場合や、`Box`のようなトレイトオブジェクトを返す場合にも重要です。いずれの場合も規則は同じで、所有者は1つ、そして`Box`がドロップされると自動的に解放されます。 ## `Rc`: シングルスレッドコードでの共有所有権 ときには、複数の所有者を持ちながら、どれが最後に使われるのか明らかでない値が必要になります。`Rc`(参照カウント)は、何人の所有者が存在するかを示す強参照カウントを保持することでこれを解決します。`Rc::clone`は基となるデータをコピーせずにそのカウントを増やし、各ドロップがそれを減らします。カウントがゼロになると、値は解放されます。 複数のワーカーが読み取る設定オブジェクトを考えてみます。各ワーカーは必要な間だけ設定を保持すべきであり、確保領域は最後のワーカーがいなくなって初めて消えるべきです。 ```rust // shared_config.rs use std::rc::Rc; #[derive(Debug)] struct Config { endpoint: String, timeout_ms: u32, } fn main() { // Rc::new moves Config onto the heap with a strong count of 1. let config = Rc::new(Config { endpoint: "https://api.example.com".to_string(), timeout_ms: 5000, }); // Rc::clone only bumps the reference count; it does not deep-copy Config. let worker_a = Rc::clone(&config); let worker_b = Rc::clone(&config); // All three handles point to the same allocation. println!("endpoint: {}", worker_a.endpoint); println!("timeout: {}", worker_b.timeout_ms); // strong_count reports how many owners currently hold the value. println!("owners: {}", Rc::strong_count(&config)); // owners: 3 } ``` `config.clone()`ではなく`Rc::clone`を明示的に呼ぶのが慣用的です。この操作が安価であり、カウンタに触れるだけであることを読み手に伝えるからです。落とし穴は、`Rc`が共有された不変の参照しか渡さないことです。共有する値を変更することはできず、スレッド間で送ることも安全ではありません。[`Rc`のドキュメント](https://doc.rust-lang.org/std/rc/struct.Rc.html)が両方の制約を明記しています。 ## `RefCell`とRustの内部可変性 Rustは通常、値が多数の不変参照を通じて共有されるか、あるいはちょうど1つの可変参照を通じて変更されるかのいずれかであることを強制し、これをコンパイル時に検査します。`RefCell`は内部可変性を提供します。同じ検査をランタイムに移すことで、共有参照を通じて値を変更できるようにするのです。`borrow()`は共有の読み取りガードを返し、`borrow_mut()`は排他的な書き込みガードを返します。規則は同一ですが、違反はコンパイルに失敗する代わりにパニックを起こします。 `Rc`と組み合わせると、シングルスレッドにおける共有可変の定石パターン`Rc>`が生まれます。同じ値をすべて更新できる複数の所有者です。 ```rust // counter.rs use std::rc::Rc; use std::cell::RefCell; // Rc gives shared ownership; RefCell allows mutation through a shared reference. type SharedCounter = Rc>; fn increment(counter: &SharedCounter) { // borrow_mut() hands out an exclusive reference, checked at runtime. *counter.borrow_mut() += 1; } fn main() { let counter: SharedCounter = Rc::new(RefCell::new(0)); let handle_a = Rc::clone(&counter); let handle_b = Rc::clone(&counter); increment(&handle_a); increment(&handle_b); increment(&counter); // borrow() gives a shared read guard; the value is now 3. println!("count = {}", counter.borrow()); // count = 3 } ``` > **RefCellは借用チェックをランタイムに移す** > > 別の`borrow()`または`borrow_mut()`ガードがまだ生きている間に`borrow_mut()`を呼ぶと、`already borrowed: BorrowMutError`でパニックします。安全性の保証は保たれますが、コンパイラなら捕捉していたはずのロジックのバグがランタイムのクラッシュになってしまいます。ガードは短命に保ち、同じ`RefCell`に再び入り込む可能性のある関数呼び出しをまたいで借用を保持することは避けるべきです。 内部可変性は参照サイクルが潜む場所でもあります。互いを指し示す2つの`Rc>`の値は、互いの強参照カウントを永遠にゼロより上に保ち続けるため、どちらも決して解放されません。解決策は`Weak`です。これは強参照カウントに影響しない非所有のハンドルです。定番の解説は[Learn Rust With Entirely Too Many Linked Lists](https://rust-unofficial.github.io/too-many-lists/)にあります。 ## `Arc`: 並行処理のためのスレッドセーフな参照カウント `Arc`(アトミック参照カウント)は`Rc`のマルチスレッド版の兄弟です。公開APIはほぼ同一ですが、カウンタがアトミック操作を使うため、複数のスレッドから同時に`Arc`をクローンしたりドロップしたりしても正しく動作します。このアトミック性こそが、共有がスレッド境界を越えるときにのみ`Arc`が選ばれる理由です。アトミックなインクリメントは、`Rc`が使う通常のものより測定可能なほど遅いのです。 `Arc`も依然として不変参照しか渡さないため、スレッドをまたいだ変更には同期プリミティブが必要です。通常のパートナーは`Mutex`であり、並行コードにおいて`Rc>`に対応する`Arc>`パターンを与えます。下の例では、共有された合計をそれぞれ1000回ずつインクリメントする4つのスレッドを生成します。 ```rust // parallel_sum.rs use std::sync::{Arc, Mutex}; use std::thread; fn main() { // Arc is safe to share across threads; Mutex serializes access to the inner value. let total = Arc::new(Mutex::new(0u64)); let mut handles = Vec::new(); for _ in 0..4 { // Each thread gets its own Arc handle (an atomic count bump). let total = Arc::clone(&total); handles.push(thread::spawn(move || { for _ in 0..1000 { // lock() blocks until the mutex is free, then returns a guard. let mut value = total.lock().unwrap(); *value += 1; } })); } for handle in handles { handle.join().unwrap(); // wait for every thread before reading } println!("total = {}", *total.lock().unwrap()); // total = 4000 } ``` コンパイラが境界を代わりに強制してくれます。`Rc`は`Send`ではないため、`thread::spawn`にムーブしようとするとコンパイルに失敗し、`Arc`へと導かれます。シングルスレッド内で`Arc`に手を伸ばすと、決して使わないアトミック操作の代償を払うだけです。[`Arc`のドキュメント](https://doc.rust-lang.org/std/sync/struct.Arc.html)がメモリ順序の保証を詳しく扱っており、`Arc>`は[Tokioを用いた非同期Rust](/blog/rust/rust-async-await-tokio-futures-concurrency)における共有状態の多くを支えています。 ## Box、Rc、Arc、RefCell: それぞれの使いどころ 4つの型は2つの軸で組み合わさります。値が何人の所有者を持つか、そして共有ハンドルを通じて変更できるかどうかです。次の表がトレードオフをまとめています。 | 型 | 所有権 | 共有ハンドル経由の変更 | スレッドセーフ | オーバーヘッド | |------|-----------|----------------------------|-------------|----------| | `Box` | 単一 | 不可 | `T: Send`なら可 | ヒープ確保のみ | | `Rc` | 共有 | 不可 | 不可 | 非アトミックなカウント | | `Arc` | 共有 | 不可 | 可 | アトミックなカウント | | `RefCell` | 単一 | 可(ランタイム検査) | 不可 | ランタイムの借用フラグ | 実践的な決定木は短いものです。ヒープ上に所有者が1つ必要なら`Box`。1つのスレッドで多数の所有者が必要なら`Rc`。スレッドをまたいで多数の所有者が必要なら`Arc`。共有された値を変更する必要があるなら、内側の型を`RefCell`(シングルスレッド)または`Mutex`(マルチスレッド)で包みます。[参照とポインタ型](https://www.lurklurk.org/effective-rust/)に関するEffective Rustの指針も同じ階層構造にたどり着いています。最も権限の小さい選択肢から始め、コンパイラが強制したときにのみ機能を追加していきます。 ## Rustスマートポインタの面接質問 スマートポインタは信頼できる面接トピックです。候補者が構文だけでなく所有権を理解しているかを試すからです。以下の質問は頻出であり、さらに多くは[Rustスマートポインタ面接モジュール](/technologies/rust/interview-questions/smart-pointers)に集められています。 **`Rc`と`Arc`の違いは何ですか。** どちらも参照カウントを通じて共有所有権を提供します。`Rc`は非アトミックなカウンタを使い、単一スレッドに限定されます。`Arc`はアトミック操作を使い、わずかなランタイムコストと引き換えにスレッド間で共有できます。コンパイラがこの区分を強制します。`Rc`は`Send`でも`Sync`でもないため、スレッド境界を越えられません。 **なぜ`Rc`と`RefCell`を組み合わせるのですか。** `Rc`は共有所有権を与えますが、アクセスは不変に限られます。`RefCell`は内部可変性を追加し、ランタイムの借用チェックとともに、所有者が共有参照を通じて値を変更できるようにします。両者を合わせた`Rc>`が、シングルスレッドにおける共有可変の慣用的な構成要素です。 **Rustで参照カウントはどのようにメモリをリークさせ得ますか。** 互いを参照する2つの`Rc`(または`Arc`)の値はサイクルを形成し、その強参照カウントは決してゼロに達しないため、確保領域は解放されません。非所有の参照を保持する`Weak`でサイクルを断ち切ると、正しい後片付けが回復します。 **`Box`が`Rc`より好ましいのはどのような場合ですか。** 単一の所有者で十分なときは常にそうです。`Box`には参照カウントのオーバーヘッドがないため、ヒープ確保、再帰的な型、トレイトオブジェクトのデフォルトとなります。真の共有所有権が必要なときにのみ`Rc`に手を伸ばします。 ## まとめ Rustのスマートポインタは、所有権を制約から組み合わせ可能な構成要素の集合へと変えます。どれを選ぶかは、問題の形から直接導かれます。 - 値がヒープ上に単一の所有者を必要とするとき、再帰的な型がサイズの固定を必要とするとき、あるいは関数がトレイトオブジェクトを返すときは`Box`を選びます。 - 1つのスレッド上で複数の所有者には`Rc`を使い、所有権がスレッド境界を越えた瞬間に`Arc`へ切り替えます。 - シングルスレッドコードでの内部可変性には`RefCell`を、その並行版には`Mutex`を加え、借用ガードとロックガードは短命に保ちます。 - 意図的に組み合わせます。1つのスレッド上の共有可変状態には`Rc>`、スレッドをまたぐ場合には`Arc>`です。 - カウント付きポインタ同士の参照サイクルに注意し、リークを避けるために`Weak`で断ち切ります。 - 最も権限の小さい型をデフォルトとし、より多くの機能が必要になったときはコンパイラに教えてもらいます。 --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/ja/blog/rust/rust-smart-pointers-box-rc-arc-refcell