---
title: Box, Rc, Arc
url: https://doc.liz6.com/rust/04-smart-pointers-and-memory/01-Box-Rc-Arc
locale: zh
area: rust
tags:
- rust
- 智能指针与内存
date: 2026-06-30
modified: 2026-06-30
description: Box 是单一所有者的堆分配(最简单),Rc 是单线程引用计数(共享所有权),Arc 是多线程原子引用计数。Weak 打破引用循环,内存布局上 Rc/Arc 的值和引用计数在堆上紧邻分配——理解这三者就是理解 Rust 的"不用 GC 但有引用计数"。
---

# Box, Rc, Arc

> Box 是单一所有者的堆分配(最简单),Rc 是单线程引用计数(共享所有权),Arc 是多线程原子引用计数。Weak 打破引用循环,内存布局上 Rc/Arc 的值和引用计数在堆上紧邻分配——理解这三者就是理解 Rust 的"不用 GC 但有引用计数"。

## Box<T>: 单一所有者的堆分配

```rust
let b = Box::new(42);               // 在堆上分配一个 i32
// stack: [8-byte ptr] → heap: [4-byte value: 42]
```

`Box<T>` 是 Rust 中最简单的堆分配——单一所有者，无引用计数。当 `b` 离开作用域时，堆内存被释放。与 C++ 的 `std::unique_ptr<T>` 相似，但 Rust 版本不需要 move semantics 关键字——所有权在编译时保证。

### 何时用 Box

1. **递归类型**: Rust 必须在编译期知道类型大小。`enum List { Cons(i32, List), Nil }` 递归导致无限大小。解决方案：`Cons(i32, Box<List>)`——Box 大小固定（指针，8B），递归通过堆间接解决
2. **Trait object**: `Box<dyn Display>` 存储任意实现了 Display 的类型
3. **大值移栈到堆**: 如果有一个 `[u8; 1000000]`，用 `Box::new([0u8; 1000000])` 只在栈上放 8 字节指针
4. **DST (dynamically sized type)**: `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: data]
Rc<T>: ptr → 指向 T 的地址 (实际指向 strong_count 前面)
```

`Rc::clone` 只增加 strong_count——数据没有被复制。当最后一个 `Rc<T>` 被 drop → 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);            // atomic increment → thread-safe
std::thread::spawn(move || {
    println!("{:?}", b);           // b can be moved into thread
});
```

与 Rc 内部结构相同，但 strong/weak count 操作用 `atomic` 指令。`Arc::clone` 比 `Rc::clone` 慢约 10x（atomic increment vs non-atomic increment），但对于堆分配来说仍是极快的（~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**: Chapter 15.1-15.4
- **Rustonomicon**: Arc atomicity, reference counting

*Keywords: Box, Rc, Arc, Weak, reference counting, heap allocation, recursive type, DST, Drop, atomic*
