Rust 智能指针详解:Box、Rc、Arc 与 RefCell
·
目录
📝 摘要
智能指针(Smart Pointers)是 Rust 中实现高级内存管理模式的关键工具。它们不仅提供了指针的功能,还附带了额外的元数据和能力。本文将深入讲解 Box<T>、Rc<T>、Arc<T>、RefCell<T> 等核心智能指针的使用场景、内部机制以及性能考量,帮助读者在复杂场景下做出正确的设计选择。
一、智能指针概述
1.1 什么是智能指针?
智能指针 vs 普通引用:

特性对比表:
| 类型 | 所有权 | 线程安全 | 运行时开销 | 使用场景 |
|---|---|---|---|---|
&T |
借用 | 是 | 零 | 默认选择 |
Box<T> |
独占 | 是 | 堆分配 | 递归类型 |
Rc<T> |
共享 | 否 | 引用计数 | 单线程共享 |
Arc<T> |
共享 | 是 | 原子计数 | 多线程共享 |
RefCell<T> |
独占 | 否 | 运行时检查 | 内部可变性 |
二、Box:堆上的数据
2.1 基础用法
fn main() {
// 在堆上分配单个值
let b = Box::new(5);
println!("b = {}", b);
// Box 拥有数据的所有权
let x = Box::new(String::from("hello"));
// let y = x; // 移动所有权
// println!("{}", x); // ❌ 编译错误
// 自动解引用
let boxed = Box::new(10);
let value = *boxed + 5;
println!("value = {}", value);
}
2.2 递归类型
// ❌ 编译错误:无限大小
// enum List {
// Cons(i32, List),
// Nil,
// }
// ✓ 使用 Box 解决
enum List {
Cons(i32, Box<List>),
Nil,
}
use List::{Cons, Nil};
fn main() {
let list = Cons(1,
Box::new(Cons(2,
Box::new(Cons(3,
Box::new(Nil)
))
))
);
print_list(&list);
}
fn print_list(list: &List) {
match list {
Cons(value, next) => {
print!("{} -> ", value);
print_list(next);
},
Nil => println!("Nil"),
}
}
内存布局:
栈 堆
┌─────────────┐
│ list │ ┌─────────────┐
│ Cons(1, ptr)├────────>│ Cons(2, ptr)├───┐
└─────────────┘ └─────────────┘ │
↓
┌─────────────┐
│ Cons(3, ptr)├───┐
└─────────────┘ │
↓
┌─────────────┐
│ Nil │
└─────────────┘
2.3 trait 对象
trait Animal {
fn make_sound(&self) -> &str;
}
struct Dog;
struct Cat;
impl Animal for Dog {
fn make_sound(&self) -> &str {
"Woof!"
}
}
impl Animal for Cat {
fn make_sound(&self) -> &str {
"Meow!"
}
}
fn main() {
// Box 用于 trait 对象
let animals: Vec<Box<dyn Animal>> = vec![
Box::new(Dog),
Box::new(Cat),
Box::new(Dog),
];
for animal in animals.iter() {
println!("{}", animal.make_sound());
}
}
三、Rc:单线程引用计数
3.1 共享所有权
use std::rc::Rc;
fn main() {
let a = Rc::new(String::from("hello"));
println!("引用计数: {}", Rc::strong_count(&a)); // 1
let b = Rc::clone(&a);
println!("引用计数: {}", Rc::strong_count(&a)); // 2
{
let c = Rc::clone(&a);
println!("引用计数: {}", Rc::strong_count(&a)); // 3
}
println!("引用计数: {}", Rc::strong_count(&a)); // 2
}
3.2 共享数据结构
use std::rc::Rc;
enum List {
Cons(i32, Rc<List>),
Nil,
}
use List::{Cons, Nil};
fn main() {
let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil)))));
let b = Cons(3, Rc::clone(&a));
let c = Cons(4, Rc::clone(&a));
println!("a 的引用计数: {}", Rc::strong_count(&a)); // 3
}
内存结构:
b: Cons(3, ─┐
↓
a: Cons(5, Rc) → Cons(10, Rc) → Nil
↑
c: Cons(4, ─┘
引用计数: 3
3.3 弱引用(Weak)
use std::rc::{Rc, Weak};
use std::cell::RefCell;
#[derive(Debug)]
struct Node {
value: i32,
parent: RefCell<Weak<Node>>,
children: RefCell<Vec<Rc<Node>>>,
}
fn main() {
let leaf = Rc::new(Node {
value: 3,
parent: RefCell::new(Weak::new()),
children: RefCell::new(vec![]),
});
println!("leaf 强引用计数: {}", Rc::strong_count(&leaf)); // 1
println!("leaf 弱引用计数: {}", Rc::weak_count(&leaf)); // 0
{
let branch = Rc::new(Node {
value: 5,
parent: RefCell::new(Weak::new()),
children: RefCell::new(vec![Rc::clone(&leaf)]),
});
*leaf.parent.borrow_mut() = Rc::downgrade(&branch);
println!("branch 强引用计数: {}", Rc::strong_count(&branch)); // 1
println!("branch 弱引用计数: {}", Rc::weak_count(&branch)); // 1
println!("leaf 强引用计数: {}", Rc::strong_count(&leaf)); // 2
}
println!("leaf 父节点: {:?}", leaf.parent.borrow().upgrade());
}
四、Arc:线程安全的引用计数
4.1 多线程共享数据
use std::sync::Arc;
use std::thread;
fn main() {
let data = Arc::new(vec![1, 2, 3, 4, 5]);
let mut handles = vec![];
for i in 0..5 {
let data_clone = Arc::clone(&data);
let handle = thread::spawn(move || {
let sum: i32 = data_clone.iter().sum();
println!("线程 {} 计算和: {}", i, sum);
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("最终引用计数: {}", Arc::strong_count(&data));
}
4.2 Arc + Mutex 组合
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let counter = Arc::clone(&counter);
let handle = thread::spawn(move || {
let mut num = counter.lock().unwrap();
*num += 1;
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("最终结果: {}", *counter.lock().unwrap());
}
4.3 性能考量
use std::sync::Arc;
use std::thread;
use std::time::Instant;
fn benchmark_arc() {
let data = Arc::new(vec![0; 1_000_000]);
let start = Instant::now();
let handles: Vec<_> = (0..4)
.map(|_| {
let data = Arc::clone(&data);
thread::spawn(move || {
let _sum: i32 = data.iter().sum();
})
})
.collect();
for handle in handles {
handle.join().unwrap();
}
println!("Arc 用时: {:?}", start.elapsed());
}
fn main() {
benchmark_arc();
}
五、RefCell:内部可变性
5.1 运行时借用检查
use std::cell::RefCell;
fn main() {
let data = RefCell::new(5);
// 不可变借用
let r1 = data.borrow();
let r2 = data.borrow();
println!("r1 = {}, r2 = {}", r1, r2);
drop(r1);
drop(r2);
// 可变借用
let mut r3 = data.borrow_mut();
*r3 += 1;
println!("修改后: {}", *r3);
drop(r3);
// ❌ 运行时panic
// let r4 = data.borrow();
// let r5 = data.borrow_mut(); // panic: 已存在不可变借用
}
5.2 Rc + RefCell 组合
use std::rc::Rc;
use std::cell::RefCell;
#[derive(Debug)]
struct Node {
value: i32,
next: Option<Rc<RefCell<Node>>>,
}
fn main() {
let first = Rc::new(RefCell::new(Node {
value: 1,
next: None,
}));
let second = Rc::new(RefCell::new(Node {
value: 2,
next: None,
}));
// 修改 first 的 next 指向 second
first.borrow_mut().next = Some(Rc::clone(&second));
// 修改 second 的值
second.borrow_mut().value = 20;
println!("first: {:?}", first);
}
5.3 实战:可变计数器
use std::cell::RefCell;
struct Counter {
count: RefCell<i32>,
}
impl Counter {
fn new() -> Self {
Counter {
count: RefCell::new(0),
}
}
fn increment(&self) {
*self.count.borrow_mut() += 1;
}
fn get(&self) -> i32 {
*self.count.borrow()
}
}
fn main() {
let counter = Counter::new();
counter.increment();
counter.increment();
counter.increment();
println!("计数: {}", counter.get());
}
六、Cell:简单内部可变性
use std::cell::Cell;
struct Config {
debug: Cell<bool>,
}
impl Config {
fn new() -> Self {
Config {
debug: Cell::new(false),
}
}
fn set_debug(&self, value: bool) {
self.debug.set(value);
}
fn is_debug(&self) -> bool {
self.debug.get()
}
}
fn main() {
let config = Config::new();
println!("调试模式: {}", config.is_debug());
config.set_debug(true);
println!("调试模式: {}", config.is_debug());
}
七、性能对比与选择指南
7.1 性能基准测试
use std::rc::Rc;
use std::sync::Arc;
use std::time::Instant;
fn benchmark() {
const N: usize = 1_000_000;
// Box 性能
let start = Instant::now();
for _ in 0..N {
let _b = Box::new(42);
}
println!("Box: {:?}", start.elapsed());
// Rc 性能
let start = Instant::now();
let rc = Rc::new(42);
for _ in 0..N {
let _clone = Rc::clone(&rc);
}
println!("Rc clone: {:?}", start.elapsed());
// Arc 性能
let start = Instant::now();
let arc = Arc::new(42);
for _ in 0..N {
let _clone = Arc::clone(&arc);
}
println!("Arc clone: {:?}", start.elapsed());
}
fn main() {
benchmark();
}
7.2 选择决策树

选择指南表:
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 递归数据结构 | Box<T> |
固定大小 |
| 单线程共享 | Rc<T> |
低开销 |
| 多线程共享 | Arc<T> |
线程安全 |
| 共享+修改(单线程) | Rc<RefCell<T>> |
内部可变 |
| 共享+修改(多线程) | Arc<Mutex<T>> |
线程安全 |
八、实战案例
8.1 案例1:图数据结构
use std::rc::{Rc, Weak};
use std::cell::RefCell;
#[derive(Debug)]
struct Node {
value: i32,
edges: RefCell<Vec<Rc<Node>>>,
parent: RefCell<Weak<Node>>,
}
impl Node {
fn new(value: i32) -> Rc<Self> {
Rc::new(Node {
value,
edges: RefCell::new(vec![]),
parent: RefCell::new(Weak::new()),
})
}
fn add_edge(&self, node: &Rc<Node>) {
self.edges.borrow_mut().push(Rc::clone(node));
*node.parent.borrow_mut() = Rc::downgrade(&Rc::new(Node {
value: self.value,
edges: RefCell::new(vec![]),
parent: RefCell::new(Weak::new()),
}));
}
}
fn main() {
let node1 = Node::new(1);
let node2 = Node::new(2);
let node3 = Node::new(3);
node1.add_edge(&node2);
node1.add_edge(&node3);
println!("Node1 edges: {}", node1.edges.borrow().len());
}
8.2 案例2:线程池
use std::sync::{Arc, Mutex};
use std::thread;
use std::sync::mpsc;
type Job = Box<dyn FnOnce() + Send + 'static>;
struct ThreadPool {
workers: Vec<Worker>,
sender: mpsc::Sender<Job>,
}
impl ThreadPool {
fn new(size: usize) -> Self {
let (sender, receiver) = mpsc::channel();
let receiver = Arc::new(Mutex::new(receiver));
let mut workers = Vec::with_capacity(size);
for id in 0..size {
workers.push(Worker::new(id, Arc::clone(&receiver)));
}
ThreadPool { workers, sender }
}
fn execute<F>(&self, f: F)
where
F: FnOnce() + Send + 'static,
{
let job = Box::new(f);
self.sender.send(job).unwrap();
}
}
struct Worker {
id: usize,
thread: thread::JoinHandle<()>,
}
impl Worker {
fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Job>>>) -> Self {
let thread = thread::spawn(move || loop {
let job = receiver.lock().unwrap().recv();
match job {
Ok(job) => {
println!("Worker {} 执行任务", id);
job();
},
Err(_) => break,
}
});
Worker { id, thread }
}
}
fn main() {
let pool = ThreadPool::new(4);
for i in 0..8 {
pool.execute(move || {
println!("任务 {} 完成", i);
thread::sleep(std::time::Duration::from_secs(1));
});
}
thread::sleep(std::time::Duration::from_secs(3));
}
九、总结与讨论
智能指针是 Rust 实现高级内存管理的核心工具:
✅ 灵活性:支持多种所有权模式
✅ 安全性:编译期+运行时双重保障
✅ 高性能:针对场景优化
✅ 可组合:Rc+RefCell, Arc+Mutex
核心概念总结:

讨论问题:
- 何时应该使用
Rc<RefCell<T>>而非重新设计数据结构? - Arc 的性能开销在什么场景下可接受?
- 如何避免 Rc 造成的循环引用?
欢迎分享经验!💬
参考链接
- Rust Book - Smart Pointers:https://doc.rust-lang.org/book/ch15-00-smart-pointers.html
- std::rc 文档:https://doc.rust-lang.org/std/rc/
- std::sync::Arc 文档:https://doc.rust-lang.org/std/sync/struct.Arc.html
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)