16 rust基础- 智能指针(smart pointer)
✅ 智能指针(Smart Pointer) 🧠
智能指针不仅像普通指针一样存储数据的内存地址,还具有 自动管理内存 的功能,包括:
- 资源分配与释放(
drop)。 - 引用计数(
Rc、Arc)。 - 栈、堆数据的自动管理。
📚 常见的智能指针类型
1️⃣ Box<T> — 将数据存储在堆上
Box<T>将数据存储在 堆(heap),指向堆数据的指针存储在 栈(stack)。- 适合当数据 大小未知 或 需要移动大量数据 的情况。
- 用法
let b = Box::new(5);
println!("b = {}", b);
- 优点
- 移动大数据时仅移动指针,减少开销。
- 可存储递归类型(例如链表)。
- 释放内存
Box<T>会在超出作用域时释放堆上的数据。
2️⃣ Rc<T> — 引用计数(Reference Counted)
Rc<T>允许 多个所有者 共享同一数据的所有权。- 通过 引用计数 机制跟踪
Rc的引用数量。 - 仅适用于 单线程 场景。
- 用法
use std::rc::Rc;
let a = Rc::new(5);
let b = Rc::clone(&a);
println!("Reference count = {}", Rc::strong_count(&a)); // 2
- 优点
- 允许多个所有者。
- 释放数据时不需要手动管理。
- 缺点
- 不能用于并发场景。
- 循环引用可能导致 内存泄漏。
3️⃣ Arc<T> — 原子引用计数(Atomic Reference Counted)
Arc<T>是Rc<T>的 线程安全 版本,适用于 多线程 环境。- 使用原子操作管理引用计数,支持并发访问。
- 用法
use std::sync::Arc;
use std::thread;
let a = Arc::new(5);
let a1 = Arc::clone(&a);
let handle = thread::spawn(move || {
println!("a1 = {}", a1);
});
handle.join().unwrap();
- 优点
- 适合并发场景,支持多线程。
- 引用计数自动管理内存。
- 缺点
- 比
Rc性能略低(由于原子操作)。
- 比
4️⃣ RefCell<T> — 运行时可变性
RefCell<T>允许在 运行时 修改不可变数据(&T)。- 通过 内部可变性 机制实现:
borrow()获取不可变引用。borrow_mut()获取可变引用。
- 用法
use std::cell::RefCell;
let data = RefCell::new(5);
*data.borrow_mut() += 1;
println!("data = {:?}", data.borrow()); // data = 6
- 优点
- 允许在不可变上下文中修改数据。
- 在编译时绕过可变性检查。
- 缺点
- 运行时违反借用规则,可能导致 panic。
- 适用于单线程场景。
5️⃣ Mutex<T> — 互斥锁(Mutual Exclusion)
Mutex<T>提供 线程安全的内部可变性。- 适合 多线程 访问共享数据。
- 用法
use std::sync::{Arc, Mutex};
use std::thread;
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!("Result: {}", *counter.lock().unwrap()); // Result: 10
- 优点
- 线程安全,防止数据竞争。
- 缺点
lock()可能引发死锁(需注意锁的使用)。- 需要显式处理
unwrap()。
6️⃣ Cow<T> — 克隆-写时拷贝(Clone-On-Write)
Cow<T>允许延迟克隆,只有在 数据修改时 进行克隆。- 常用于优化不可变数据的写时拷贝。
- 用法
use std::borrow::Cow;
fn process(input: &mut Cow<[i32]>) {
if input.iter().any(|&x| x < 0) {
input.to_mut().iter_mut().for_each(|x| *x = x.abs());
}
}
let vec = vec![1, -2, 3];
let mut input = Cow::from(&vec);
process(&mut input);
println!("Processed: {:?}", input);
- 优点
- 节省内存,减少不必要的拷贝。
- 缺点
- 需要手动检查是否需要克隆。
📝 对比总结
| 智能指针 | 特点 | 线程安全 | 可变性 | 适用场景 |
|---|---|---|---|---|
Box<T> |
堆分配数据 | ❌ | ❌ | 存储递归数据或大数据 |
Rc<T> |
引用计数 | ❌ | ❌ | 单线程,多所有者共享数据 |
Arc<T> |
线程安全的引用计数 | ✅ | ❌ | 多线程共享数据 |
RefCell<T> |
运行时内部可变性 | ❌ | ✅ | 单线程内的可变数据管理 |
Mutex<T>/RwLock<T> |
线程安全的内部可变性 | ✅ | ✅ | 多线程访问共享数据 |
Cow<T> |
克隆-写时拷贝 | ✅ | ✅ | 数据只在需要时进行修改 |
🕹️ 选择指南
- ✅ 使用
Box<T>:当需要 堆分配 数据时(例如递归结构)。 - ✅ 使用
Rc<T>:当需要 多个所有者,但只在 单线程 中使用。 - ✅ 使用
Arc<T>:在 多线程 中共享数据时。 - ✅ 使用
RefCell<T>:当需要在 不可变环境中修改数据。 - ✅ 使用
Mutex<T>:当多线程需要 安全地修改数据 时。 - ✅ 使用
Cow<T>:数据 无需频繁拷贝 时,优化内存使用。

浙公网安备 33010602011771号