文章
Rust中的智能指针
在 Rust 中,智能指针(Smart Pointers)是管理内存和所有权的核心工具。它们不仅能自动管理内存的生命周期,还能提供额外的行为(如引用计数、内部可变性等)
目录
- 在 Rust 中,智能指针(Smart Pointers)是管理内存和所有权的核心工具。它们不仅能自动管理内存的生命周期,还能提供额外的行为(如引用计数、内部可变性等)。以下是 Rust 中常见智能指针的区别、使用场景及示例:
- 🧠 常见智能指针及其使用场景
- 1. Box<T>:单一所有权的堆分配
- fn main() { let boxed = Box::new(42); // 将整数 42 分配到堆上 let list = Box::new(vec![1, 2, 3]); // 将 Vec 分配到堆上 } ```
- 2. Rc<T>:引用计数(单线程共享所有权)
- fn main() { let data = Rc::new(42); let a = Rc::clone(&data); // 引用计数 +1 let b = Rc::clone(&data); // 引用计数 +1 // 当 a 和 b 离开作用域时,计数归零,内存释放 } ```
- 3. Arc<T>:原子引用计数(多线程共享所有权)
- 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>
- RefCell<T>
- 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)
- 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>
- RwLock<T>
- 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; } } ```
- 总结:智能指针的核心区别
- 何时使用哪种指针?
- 📎 参考文章
在 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> | 共享所有权 | 是 | 读写分离 | 读多写少的并发场景 |
何时使用哪种指针?#
- 单一所有权且无需共享 →
Box<T>。 - 单线程共享不可变数据 →
Rc<T>。 - 多线程共享不可变数据 →
Arc<T>。 - 单线程内部可变性 →
Cell<T>(简单类型)或RefCell<T>(复杂类型)。 - 多线程内部可变性 →
Mutex<T>或RwLock<T>。 理解这些智能指针的差异是编写安全高效 Rust 代码的关键!