---
title: Box, Rc, Arc
url: https://doc.liz6.com/ja/rust/04-smart-pointers-and-memory/01-Box-Rc-Arc
locale: ja
area: rust
tags:
- Box
- Rc
- Arc
- Weak
- 参照カウント
- ヒープ割り当て
- 再帰型
- DST
- Drop
- 原子性
- rust
- smart-pointers-and-memory
date: 2026-06-30
modified: 2026-07-16
description: Box は単一所有者のヒープ割り当て（最もシンプル）、Rc はシングルスレッドの参照カウント（共有所有権）、Arc はマルチスレッドの原子参照カウントです。Weak は循環参照を解消し、メモリレイアウト上、Rc/Arc の値と参照カウントはヒープ上で隣接して割り当てられます——これら3つを理解することは、Rust の「GC は使わないが参照カウントがある」という仕組みを理解することに他なりません。
---

# Box, Rc, Arc

> Box は単一所有者のヒープ割り当て（最もシンプル）、Rc はシングルスレッドの参照カウント（共有所有権）、Arc はマルチスレッドの原子参照カウントです。Weak は循環参照を解消し、メモリレイアウト上、Rc/Arc の値と参照カウントはヒープ上で隣接して割り当てられます——これら3つを理解することは、Rust の「GC は使わないが参照カウントがある」という仕組みを理解することに他なりません。

## Box<T>: 単一所有者のヒープ割り当て

```rust
let b = Box::new(42);               // ヒープ上に i32 を割り当てる
// stack: [8バイトのポインタ] → heap: [4バイトの値: 42]
```

`Box<T>` は Rust で最もシンプルなヒープ割り当てです——単一所有者であり、参照カウントを持ちません。`b` がスコープを外れると、ヒープメモリは解放されます。C++ の `std::unique_ptr<T>` に似ていますが、Rust のバージョンではムーブセマンティクスのキーワードは不要です——所有権はコンパイル時に保証されます。

### Box の使用タイミング

1. **再帰型**: Rust では型サイズをコンパイル時に知らなければなりません。`enum List { Cons(i32, List), Nil }` とすると再帰によりサイズが無限大になります。解決策：`Cons(i32, Box<List>)`——Box のサイズは固定（ポインタ、8バイト）であり、再帰はヒープ経由で間接的に解決されます
2. **トレイトオブジェクト**: `Box<dyn Display>` は、Display を実装した任意の型を格納できます
3. **大きな値をスタックからヒープへ**: `[u8; 1000000]` のような大きな値がある場合、`Box::new([0u8; 1000000])` を使うと、スタック上には 8 バイトのポインタしか置かれなくなります
4. **DST (動的サイズ型)**: `Box<[T]>`, `Box<str>`, `Box<dyn Trait>`

## Rc<T>: シングルスレッドの参照カウント

```rust
use std::rc::Rc;
let a = Rc::new(vec![1, 2, 3]);     // strong_count = 1
let b = Rc::clone(&a);              // strong_count = 2、Vec のデータはコピーされない！
let c = Rc::clone(&a);              // strong_count = 3
```

内部レイアウト（ヒープ上、隣接して割り当て）：
```
heap: [strong_count: usize(8B)] [weak_count: usize(8B)] [T: データ]
Rc<T>: ptr → T のアドレスを指す（実際には strong_count の手前を指す）
```

`Rc::clone` は strong_count を増やすだけで、データはコピーされません。最後の `Rc<T>` がドロップされると → strong_count = 0 → データが解放されます。Rc は `Send` を実装していません——その参照カウント操作はアトミックではなく、スレッド間で使用するとデータ競合を引き起こします。

## Arc<T>: マルチスレッドの参照カウント

```rust
use std::sync::Arc;
let a = Arc::new(vec![1, 2, 3]);
let b = Arc::clone(&a);            // 原子インクリメント → スレッドセーフ
std::thread::spawn(move || {
    println!("{:?}", b);           // b はスレッド内にムーブ可能
});
```

内部構造は Rc と同じですが、strong/weak カウントの操作は `atomic` 命令を使用します。`Arc::clone` は `Rc::clone` より約 10 倍遅い（アトミックインクリメント vs 非アトミックインクリメント）ですが、ヒープ割り当てにとっては依然として非常に高速です（約 10ns vs 約 1ns）。

## Weak<T>: 循環参照の防止

```rust
use std::rc::{Rc, Weak};
let strong = Rc::new(42);
let weak: Weak<_> = Rc::downgrade(&strong);  // strong_count は増加しない

drop(strong);                        // strong_count = 0 → データ解放
assert!(weak.upgrade().is_none());   // weak はデータが解放されたことを検知 → None
```

古典的な用途：木構造において、親ノードが子ノードを指す `Weak` 参照を保持します。もし `Rc` を保持すると、循環参照になります——親が子を参照し、子が親を参照し、どちらも解放できません。`Weak` はデータの解放を防ぐものではなく、`upgrade()` メソッドを通じて一時的にデータへのアクセス権を取得します（None を返す可能性があります）。

## 参考

- **Rust Book**: 第15章 15.1〜15.4
- **Rustonomicon**: Arc の原子性、参照カウント

*Keywords: Box, Rc, Arc, Weak, 参照カウント, ヒープ割り当て, 再帰型, DST, Drop, 原子性*
