16 rust基础- 智能指针(smart pointer)

智能指针(Smart Pointer) 🧠

智能指针不仅像普通指针一样存储数据的内存地址,还具有 自动管理内存 的功能,包括:

  • 资源分配与释放(drop)。
  • 引用计数(RcArc)。
  • 栈、堆数据的自动管理。

📚 常见的智能指针类型


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>:数据 无需频繁拷贝 时,优化内存使用。
posted @ 2025-03-23 16:44  代码世界faq  阅读(45)  评论(0)    收藏  举报