智能指针
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)); // 2
3. 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 mutably
Cell<T>
Cell
更轻量,适合存放 Copy
类型数据(比如整数、布尔值)。
use std::cell::Cell;
let c = Cell::new(5);
c.set(10);
println!("c = {}", c.get());
它不返回引用,只操作值的复制。