返回文章列表

文章

Rust中的智能指针

在 Rust 中,智能指针(Smart Pointers)是管理内存和所有权的核心工具。它们不仅能自动管理内存的生命周期,还能提供额外的行为(如引用计数、内部可变性等)

目录
  1. 在 Rust 中,智能指针(Smart Pointers)是管理内存和所有权的核心工具。它们不仅能自动管理内存的生命周期,还能提供额外的行为(如引用计数、内部可变性等)。以下是 Rust 中常见智能指针的区别、使用场景及示例:
  2. 🧠 常见智能指针及其使用场景
  3. 1. Box<T>:单一所有权的堆分配
  4. fn main() { let boxed = Box::new(42); // 将整数 42 分配到堆上 let list = Box::new(vec![1, 2, 3]); // 将 Vec 分配到堆上 } ```
  5. 2. Rc<T>:引用计数(单线程共享所有权)
  6. fn main() { let data = Rc::new(42); let a = Rc::clone(&data); // 引用计数 +1 let b = Rc::clone(&data); // 引用计数 +1 // 当 a 和 b 离开作用域时,计数归零,内存释放 } ```
  7. 3. Arc<T>:原子引用计数(多线程共享所有权)
  8. fn main() { let data = Arc::new(42); let data_clone = Arc::clone(&data); thread::spawn(move || { println!("{}", data_clone); // 跨线程安全访问 }).join().unwrap(); } ```
  9. 4. Cell<T> 和 RefCell<T>:内部可变性
  10. Cell<T>
  11. RefCell<T>
  12. fn main() { let ref_cell = RefCell::new(vec![1, 2, 3]); let mut borrow = ref_cell.borrow_mut(); borrow.push(4); // 修改内部 Vec // 离开作用域时自动释放借用 } ```
  13. 5. Cow<T>:写时克隆(Copy-on-Write)
  14. fn main() { let static_data = Cow::Borrowed(&[1, 2, 3]); let owned_data = Cow::Owned(vec![4, 5, 6]); process_data(static_data); process_data(owned_data); } ```
  15. 6. Mutex<T> 和 RwLock<T>:线程安全的内部可变性
  16. Mutex<T>
  17. RwLock<T>
  18. fn main() { let lock = RwLock::new(5); { let r1 = lock.read().unwrap(); // 多个读 let r2 = lock.read().unwrap(); } { let mut w = lock.write().unwrap(); // 单个写 *w += 1; } } ```
  19. 总结:智能指针的核心区别
  20. 何时使用哪种指针?
  21. 📎 参考文章

在 Rust 中,智能指针(Smart Pointers)是管理内存和所有权的核心工具。它们不仅能自动管理内存的生命周期,还能提供额外的行为(如引用计数、内部可变性等)。以下是 Rust 中常见智能指针的区别、使用场景及示例:#

🧠 常见智能指针及其使用场景#

智能指针类型何时使用
`Box`堆分配唯一所有权指针需要将数据存储在堆上、递归类型、或大对象不适合栈上分配时
`Rc`引用计数共享所有权(单线程)多个所有者共享同一份数据,但不会修改(只读共享)
`Arc`原子引用计数(多线程)多线程共享不可变数据;线程安全版 `Rc`
`RefCell`运行时可变性(单线程)在不允许 `mut` 的上下文中修改数据(内部可变性)
`Mutex`多线程可变共享多线程中多个所有者共享且需要修改数据;加锁同步
`RwLock`多读少写优化锁需要多个线程读,偶尔写的场景
`Cell`拷贝类型的内部可变性`Copy` 类型的内部可变性替代(比 `RefCell` 更轻量)
`Pin`固定内存位置实现 `Future`、self-referential 结构体等对固定地址有要求的情况

1. Box<T>:单一所有权的堆分配#

  • 用途:将数据存储在堆上,管理单一所有权。
  • 特点
    • 编译时确定大小(指针本身在栈,数据在堆)。
    • 离开作用域时自动释放堆内存。
  • 示例

fn main() { let boxed = Box::new(42); // 将整数 42 分配到堆上 let list = Box::new(vec![1, 2, 3]); // 将 Vec 分配到堆上 } ```#

2. Rc<T>:引用计数(单线程共享所有权)#

  • 用途:在单线程中共享数据所有权(多所有者)。
  • 特点
    • 通过引用计数跟踪所有者数量。
    • 不可变借用,不能用于多线程。
  • 示例

use std::rc::Rc;

fn main() { let data = Rc::new(42); let a = Rc::clone(&data); // 引用计数 +1 let b = Rc::clone(&data); // 引用计数 +1 // 当 a 和 b 离开作用域时,计数归零,内存释放 } ```#

3. Arc<T>:原子引用计数(多线程共享所有权)#

  • 用途:跨线程共享数据所有权(线程安全的 Rc<T>)。
  • 特点
    • 使用原子操作保证引用计数的线程安全。
    • 性能略低于 Rc<T>
  • 示例

use std::sync::Arc; use std::thread;

fn main() { let data = Arc::new(42); let data_clone = Arc::clone(&data); thread::spawn(move || { println!("{}", data_clone); // 跨线程安全访问 }).join().unwrap(); } ```#

4. Cell<T>RefCell<T>:内部可变性#

Cell<T>#

  • 用途:对实现了 Copy 的类型提供内部可变性。
  • 特点
    • 直接通过 get/set 修改值,无需借用检查。
    • 仅适用于 Copy 类型(如 i32, bool)。
  • 示例

use std::cell::Cell;

fn main() { let cell = Cell::new(42); cell.set(100); // 直接修改内部值 println!("{}", cell.get()); } ```

RefCell<T>#

  • 用途:对未实现 Copy 的类型提供运行时借用检查的内部可变性。
  • 特点
    • 通过 borrow()borrow_mut() 动态检查借用规则。
    • 违反借用规则时触发运行时 panic(而非编译错误)。
  • 示例

use std::cell::RefCell;

fn main() { let ref_cell = RefCell::new(vec![1, 2, 3]); let mut borrow = ref_cell.borrow_mut(); borrow.push(4); // 修改内部 Vec // 离开作用域时自动释放借用 } ```#

5. Cow<T>:写时克隆(Copy-on-Write)#

  • 用途:优化场景,避免不必要的复制。
  • 特点
    • 数据可能是借用(&T)或拥有(T)。
    • 只在需要修改时克隆数据。
  • 示例

use std::borrow::Cow;

fn process_data(data: Cow<[i32]>) { // 如果数据未修改,直接使用借用 let modified = data.to_mut(); // 需要修改时克隆 modified.push(42); }

fn main() { let static_data = Cow::Borrowed(&[1, 2, 3]); let owned_data = Cow::Owned(vec![4, 5, 6]); process_data(static_data); process_data(owned_data); } ```#

6. Mutex<T>RwLock<T>:线程安全的内部可变性#

Mutex<T>#

  • 用途:跨线程互斥访问数据。
  • 特点
    • 通过锁机制保证同一时间只有一个线程访问数据。
    • 可能引发死锁。
  • 示例

use std::sync::{Mutex, Arc}; use std::thread;

fn main() { 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());

} ```

RwLock<T>#

  • 用途:允许多个读或单个写。
  • 特点
    • 读操作可并发,写操作独占。
    • Mutex 更灵活,但可能引发写饥饿。
  • 示例

use std::sync::RwLock;

fn main() { let lock = RwLock::new(5); { let r1 = lock.read().unwrap(); // 多个读 let r2 = lock.read().unwrap(); } { let mut w = lock.write().unwrap(); // 单个写 *w += 1; } } ```#

总结:智能指针的核心区别#

指针类型所有权模型线程安全可变性典型场景
Box<T>单一所有权不可变/可变堆分配、递归类型
Rc<T>共享所有权不可变单线程多所有者
Arc<T>共享所有权不可变多线程多所有者
Cell<T>单一所有权内部可变(Copy)简单值的内部修改
RefCell<T>单一所有权内部可变运行时借用检查
Mutex<T>共享所有权互斥可变跨线程数据安全修改
RwLock<T>共享所有权读写分离读多写少的并发场景

何时使用哪种指针?#

  1. 单一所有权且无需共享Box<T>
  2. 单线程共享不可变数据Rc<T>
  3. 多线程共享不可变数据Arc<T>
  4. 单线程内部可变性Cell<T>(简单类型)或 RefCell<T>(复杂类型)。
  5. 多线程内部可变性Mutex<T>RwLock<T>。 理解这些智能指针的差异是编写安全高效 Rust 代码的关键!

📎 参考文章#