智能指针

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());

它不返回引用,只操作值的复制。

最后修改:2025 年 05 月 20 日
如果觉得我的文章对你有用,请随意赞赏