Rust 系统编程语言完全教程 / 第15章:智能指针
第15章:智能指针
15.1 什么是智能指针
智能指针(smart pointer)是一种数据结构,表现类似指针,但拥有额外的元数据和功能。
| 类型 | 所有权 | 借用检查 | 线程安全 | 用途 |
|---|---|---|---|---|
Box<T> | 独占 | 编译时 | ✅ | 堆分配 |
Rc<T> | 共享 | 编译时 | ❌ | 单线程共享 |
Arc<T> | 共享 | 编译时 | ✅ | 多线程共享 |
RefCell<T> | 独占 | 运行时 | ❌ | 内部可变性 |
Cell<T> | 独占 | 无(Copy) | ❌ | 内部可变性 |
15.2 Box
在堆上分配数据。
基本用法
fn main() {
// 在堆上分配一个 i32
let b = Box::new(5);
println!("b = {}", b); // 自动解引用
// 使用场景1:递归类型
// 使用场景2:大数据避免栈溢出
// 使用场景3:trait 对象
}
递归类型
// 链表:每个节点包含值和指向下一个节点的指针
#[derive(Debug)]
enum List {
Cons(i32, Box<List>),
Nil,
}
use List::{Cons, Nil};
fn main() {
let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
println!("{:?}", list);
// 打印链表
fn print_list(list: &List) {
match list {
Cons(val, next) => {
print!("{} -> ", val);
print_list(next);
}
Nil => println!("Nil"),
}
}
print_list(&list);
}
Box 大小
fn main() {
// Box<T> 的大小是一个指针大小(8 字节在 64 位系统上)
println!("Box<i32> 大小: {} 字节", std::mem::size_of::<Box<i32>>()); // 8
println!("Box<[i32; 1000]> 大小: {} 字节", std::mem::size_of::<Box<[i32; 1000]>>()); // 8
println!("[i32; 1000] 大小: {} 字节", std::mem::size_of::<[i32; 1000]>()); // 4000
}
15.3 Deref Trait
自定义 Deref
use std::ops::Deref;
struct MyBox<T>(T);
impl<T> MyBox<T> {
fn new(x: T) -> MyBox<T> {
MyBox(x)
}
}
impl<T> Deref for MyBox<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
fn hello(name: &str) {
println!("Hello, {}!", name);
}
fn main() {
let x = 5;
let y = MyBox::new(x);
assert_eq!(5, x);
assert_eq!(5, *y); // 通过 Deref 解引用
// Deref 强制转换
let m = MyBox::new(String::from("Rust"));
hello(&m); // MyBox<String> → &String → &str
// 等价于 hello(&(*m)[..]);
}
Deref 强制转换规则
- 当
T: Deref<Target=U>时,&T自动转换为&U - 当
T: DerefMut<Target=U>时,&mut T自动转换为&mut U
这就是为什么 &String 可以用在需要 &str 的地方。
15.4 Drop Trait
自定义清理逻辑
struct DatabaseConnection {
url: String,
}
impl DatabaseConnection {
fn new(url: &str) -> Self {
println!("连接到数据库: {}", url);
Self { url: url.to_string() }
}
fn query(&self, sql: &str) {
println!("执行查询: {}", sql);
}
}
impl Drop for DatabaseConnection {
fn drop(&mut self) {
println!("关闭数据库连接: {}", self.url);
}
}
fn main() {
let conn = DatabaseConnection::new("postgres://localhost/mydb");
conn.query("SELECT * FROM users");
// 手动提前释放
let conn2 = DatabaseConnection::new("postgres://localhost/other");
drop(conn2); // 显式调用 drop
println!("conn2 已释放");
// conn 在 main 结束时自动释放
println!("函数即将结束");
}
15.5 Rc:引用计数
基本用法
use std::rc::Rc;
fn main() {
let a = Rc::new(vec![1, 2, 3]);
// 克隆 Rc 增加引用计数,不复制数据
let b = Rc::clone(&a);
let c = Rc::clone(&a);
println!("a = {:?}", a);
println!("b = {:?}", b);
println!("引用计数: {}", Rc::strong_count(&a)); // 3
drop(b);
println!("drop b 后引用计数: {}", Rc::strong_count(&a)); // 2
}
共享链表
use std::rc::Rc;
#[derive(Debug)]
enum List {
Cons(i32, Rc<List>),
Nil,
}
fn main() {
let shared = Rc::new(List::Cons(5, Rc::new(List::Nil)));
// a 和 b 共享同一个链表尾部
let a = List::Cons(10, Rc::clone(&shared));
let b = List::Cons(20, Rc::clone(&shared));
println!("a = {:?}", a);
println!("b = {:?}", b);
println!("shared 引用计数: {}", Rc::strong_count(&shared)); // 3
}
注意:
Rc<T>仅适用于单线程。多线程请使用Arc<T>。
15.6 Arc:原子引用计数
use std::sync::Arc;
use std::thread;
fn main() {
let data = Arc::new(vec![1, 2, 3, 4, 5]);
let mut handles = vec![];
for i in 0..3 {
let data = Arc::clone(&data);
let handle = thread::spawn(move || {
let sum: i32 = data.iter().sum();
println!("线程 {}: sum = {}", i, sum);
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("引用计数: {}", Arc::strong_count(&data));
}
Rc vs Arc
| 特性 | Rc | Arc |
|---|---|---|
| 线程安全 | ❌ | ✅ |
| 性能 | 更快(非原子操作) | 较慢(原子操作) |
| 适用场景 | 单线程共享 | 多线程共享 |
| Send + Sync | ❌ | ✅ |
15.7 RefCell:内部可变性
运行时借用检查
use std::cell::RefCell;
fn main() {
let data = RefCell::new(vec![1, 2, 3]);
// 不可变借用
{
let borrowed = data.borrow();
println!("数据: {:?}", borrowed);
} // borrowed 在此释放
// 可变借用
{
let mut borrowed = data.borrow_mut();
borrowed.push(4);
borrowed.push(5);
} // borrowed 在此释放
println!("修改后: {:?}", data.borrow());
}
运行时 panic
use std::cell::RefCell;
fn main() {
let data = RefCell::new(42);
let borrow1 = data.borrow();
// let borrow2 = data.borrow_mut(); // ❌ 运行时 panic: already borrowed
println!("{}", borrow1);
drop(borrow1);
let mut borrow2 = data.borrow_mut();
*borrow2 = 100;
drop(borrow2);
println!("{}", data.borrow());
}
Rc<RefCell>:共享可变状态
use std::cell::RefCell;
use std::rc::Rc;
#[derive(Debug)]
struct SharedState {
value: i32,
history: Vec<i32>,
}
impl SharedState {
fn new(value: i32) -> Rc<RefCell<Self>> {
Rc::new(RefCell::new(Self {
value,
history: vec![value],
}))
}
fn update(state: &Rc<RefCell<Self>>, new_value: i32) {
let mut s = state.borrow_mut();
s.value = new_value;
s.history.push(new_value);
}
}
fn main() {
let state = SharedState::new(0);
// 多处共享同一状态
let observer = Rc::clone(&state);
SharedState::update(&state, 10);
SharedState::update(&state, 20);
SharedState::update(&state, 30);
// observer 可以看到所有修改
let s = observer.borrow();
println!("当前值: {}", s.value);
println!("历史: {:?}", s.history);
}
15.8 Cell
use std::cell::Cell;
fn main() {
// Cell 适用于 Copy 类型
let c = Cell::new(42);
c.set(100);
println!("Cell: {}", c.get()); // 100
// Cell 不需要借用检查,性能更好
let counter = Cell::new(0);
for _ in 0..10 {
counter.set(counter.get() + 1);
}
println!("计数: {}", counter.get()); // 10
}
Cell vs RefCell
| 特性 | Cell | RefCell |
|---|---|---|
| 类型限制 | T: Copy | 任意 T |
| 借用检查 | 无(直接替换) | 运行时 |
| 性能 | 更快 | 较慢 |
| 获取值 | .get() (Copy) | .borrow() (引用) |
| 设置值 | .set() | .borrow_mut() |
15.9 Weak:弱引用
打破循环引用。
use std::cell::RefCell;
use std::rc::{Rc, Weak};
#[derive(Debug)]
struct Node {
value: i32,
parent: RefCell<Weak<Node>>, // 弱引用避免循环
children: RefCell<Vec<Rc<Node>>>,
}
fn main() {
let leaf = Rc::new(Node {
value: 3,
parent: RefCell::new(Weak::new()),
children: RefCell::new(vec![]),
});
let branch = Rc::new(Node {
value: 5,
parent: RefCell::new(Weak::new()),
children: RefCell::new(vec![Rc::clone(&leaf)]),
});
// 设置 leaf 的 parent 为 branch 的弱引用
*leaf.parent.borrow_mut() = Rc::downgrade(&branch);
// 通过弱引用访问 parent
if let Some(parent) = leaf.parent.borrow().upgrade() {
println!("leaf 的 parent: {}", parent.value);
}
println!("branch 强引用数: {}", Rc::strong_count(&branch)); // 1
println!("branch 弱引用数: {}", Rc::weak_count(&branch)); // 1
}
15.10 业务场景示例
共享配置
use std::sync::Arc;
use std::collections::HashMap;
#[derive(Debug, Clone)]
struct AppConfig {
database_url: String,
max_connections: u32,
debug: bool,
}
struct AppState {
config: Arc<AppConfig>,
request_count: std::sync::atomic::AtomicU64,
}
impl AppState {
fn new(config: AppConfig) -> Self {
Self {
config: Arc::new(config),
request_count: std::sync::atomic::AtomicU64::new(0),
}
}
fn handle_request(&self) {
self.request_count.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
let count = self.request_count.load(std::sync::atomic::Ordering::Relaxed);
if self.config.debug {
println!("请求 #{} - DB: {}", count, self.config.database_url);
}
}
}
fn main() {
let config = AppConfig {
database_url: "postgres://localhost/mydb".to_string(),
max_connections: 10,
debug: true,
};
let state = Arc::new(AppState::new(config));
let handles: Vec<_> = (0..5)
.map(|_| {
let state = Arc::clone(&state);
std::thread::spawn(move || {
state.handle_request();
})
})
.collect();
for handle in handles {
handle.join().unwrap();
}
}
观察者模式
use std::cell::RefCell;
use std::rc::Rc;
type Observer = Rc<RefCell<dyn FnMut(i32)>>;
struct EventEmitter {
observers: Vec<Observer>,
}
impl EventEmitter {
fn new() -> Self {
Self { observers: Vec::new() }
}
fn subscribe(&mut self, observer: Observer) {
self.observers.push(observer);
}
fn emit(&self, value: i32) {
for observer in &self.observers {
(*observer.borrow_mut())(value);
}
}
}
fn main() {
let mut emitter = EventEmitter::new();
let log: Rc<RefCell<Vec<i32>>> = Rc::new(RefCell::new(Vec::new()));
let log_clone = Rc::clone(&log);
emitter.subscribe(Rc::new(RefCell::new(move |val| {
println!("观察者1: 收到 {}", val);
})));
emitter.subscribe(Rc::new(RefCell::new(move |val| {
log_clone.borrow_mut().push(val);
})));
emitter.emit(10);
emitter.emit(20);
emitter.emit(30);
println!("日志: {:?}", log.borrow());
}
15.11 本章小结
| 要点 | 说明 |
|---|---|
| Box | 堆分配,独占所有权 |
| Rc | 单线程共享,引用计数 |
| Arc | 多线程共享,原子引用计数 |
| RefCell | 内部可变性,运行时借用检查 |
| Cell | 内部可变性,Copy 类型专用 |
| Weak | 弱引用,不增加引用计数 |
| Deref | 自定义解引用行为 |
| Drop | 自定义清理逻辑 |
扩展阅读
- Rust Book - 智能指针 — 官方教程
- Rc 文档 — Rc API 参考
- Arc 文档 — Arc API 参考