智能指针
Rust 常见的智能指针
| 智能指针 | 用途与特点 |
|---|---|
Box<T> | 堆分配指针。将数据放到堆上,拥有唯一所有权。 |
Rc<T> | 引用计数指针。多个所有者,单线程共享数据。 |
Arc<T> | 线程安全引用计数指针,多线程共享数据(Arc = atomic Rc)。 |
RefCell<T> | 在运行时检查借用规则,允许“内部可变性”,常与 Rc 搭配。 |
Mutex<T> | 线程互斥锁,保证多线程写访问安全。 |
RwLock<T> | 读写锁,允许多个读者或一个写者。 |
1. Box<T>
- 功能:把值放在堆上,
Box本身在栈上存指针。 - 所有权:唯一所有权,适合递归类型或大对象堆分配。
- 示例:
let b = Box::new(5);
println!("b = {}", b);2. Rc<T>(Reference Counted)
- 功能:多所有者单线程共享数据。
- 原理:引用计数,自动计数释放。
- 限制:非线程安全,不能跨线程使用。
- 示例:
use std::rc::Rc;
let a = Rc::new(5);
let b = Rc::clone(&a);
println!("count = {}", Rc::strong_count(&a)); // 23. Arc<T>(Atomic Rc)
- 功能:线程安全的
Rc,可跨线程共享。 - 使用场景:多线程环境下共享数据。
- 示例:
use std::sync::Arc;
use std::thread;
let data = Arc::new(5);
let data2 = Arc::clone(&data);
thread::spawn(move || {
println!("data = {}", data2);
}).join().unwrap();4. RefCell<T>
- 功能:在单线程中实现“内部可变性”。
- 特点:在运行时检查借用规则,允许在不可变变量中修改数据。
- 常用搭配:
Rc<RefCell<T>>实现多所有者可变数据。 - 示例:
use std::cell::RefCell;
let x = RefCell::new(5);
* x.borrow_mut() = 10;
println!("{}", x.borrow());5. Mutex<T> 和 RwLock<T>
- 功能:多线程中互斥访问。
Mutex<T>:任意时刻只允许一个线程访问。RwLock<T>:支持多线程读,单线程写。- 示例:
use std::sync::Mutex;
let m = Mutex::new(5);
{
let mut num = m.lock().unwrap();
*num = 10;
}
println!("{:?}", m.lock().unwrap());内部可变性
通常,Rust 的借用规则要求:
- 不可变引用
&T不能修改数据; - 可变引用
&mut T独占且允许修改数据。
但是有时候我们想突破这个限制:
“我有一个不可变的变量,但它内部的某些数据需要可变修改。”
例如缓存、懒加载、引用计数内部状态等。
核心思想
变量本身是不可变的,但内部状态允许“借用检查”绕过不可变限制,在运行时动态检测借用规则。 使用包装后的指针
这就是内部可变性:外部不可变,内部可变。
原理
包装了一个UnsafeCell 使用大量unsafe指针操作 但已经证明操作是安全的
pub struct RefCell<T: ?Sized> {
borrow: Cell<BorrowFlag>,
// Stores the location of the earliest currently active borrow.
// This gets updated whenever we go from having zero borrows
// to having a single borrow. When a borrow occurs, this gets included
// in the generated `BorrowError`/`BorrowMutError`
#[cfg(feature = "debug_refcell")]
borrowed_at: Cell<Option<&'static crate::panic::Location<'static>>>,
value: UnsafeCell<T>,
}主要实现类型
| 类型 | 线程安全 | 功能描述 |
|---|---|---|
Cell<T> | 否 | 允许修改 Copy 类型的值,提供 get/set,无借用检查 |
RefCell<T> | 否 | 允许修改非 Copy 类型的值,运行时借用检查,动态保证借用规则 |
Mutex<T> | 是 | 多线程互斥访问,保证线程安全的可变性 |
RwLock<T> | 是 | 多线程读写锁,支持多个读或一个写 |
RefCell<T>
RefCell 是内部可变性的典型实现,常用于单线程场景。
- 特点:允许你在不可变的
RefCell<T>上调用borrow_mut()获取可变引用。 - 借用规则由运行时检测,违反时程序会 panic。
- 常和
Rc<T>组合使用,允许多所有者共享可变数据。
代码演示
use std::cell::RefCell;
let data = RefCell::new(5);
// 通过 borrow_mut() 获取可变引用修改值
*data.borrow_mut() = 10;
// 通过 borrow() 获取不可变引用读取值
println!("data = {}", data.borrow());运行时借用检查
let data = RefCell::new(5);
let b1 = data.borrow_mut();
// 再次借用可变引用会 panic
let b2 = data.borrow_mut(); // panic: already borrowed mutablyCell<T>
Cell 更轻量,适合存放 Copy 类型数据(比如整数、布尔值)。
use std::cell::Cell;
let c = Cell::new(5);
c.set(10);
println!("c = {}", c.get());它不返回引用,只操作值的复制。