智能指针¶
📚 章节概述¶
智能指针是拥有额外元数据和功能的指针。Rust 标准库提供了多种智能指针类型,本章将介绍最常用的几种:Box、Rc、Arc、RefCell 和 Weak。
🎯 学习目标¶
- 理解智能指针的概念
- 掌握 Box 的使用场景
- 学会使用 Rc 实现多重所有权
- 理解 Arc 的线程安全特性
- 掌握 RefCell 的内部可变性
- 学会使用 Weak 避免循环引用
📖 智能指针基础¶
1.1 什么是智能指针¶
智能指针是数据结构,它们的行为类似指针,但拥有额外的元数据和功能:
- Box:堆上分配
- Rc:引用计数
- Arc:原子引用计数(线程安全)
- RefCell:运行时借用检查
1.2 智能指针与引用的区别¶
📦 Box¶
2.1 使用 Box 在堆上分配¶
2.2 Box 的使用场景¶
递归类型¶
Rust
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))))));
}
大数据转移所有权¶
Rust
struct LargeData {
data: [u8; 1000000],
}
fn main() {
let large_data = LargeData { data: [0; 1000000] };
let boxed_data = Box::new(large_data);
// 只传递指针,不复制数据
}
Trait 对象¶
Rust
trait Draw {
fn draw(&self);
}
struct Button {
width: u32,
height: u32,
}
impl Draw for Button {
fn draw(&self) {
println!("Drawing button {}x{}", self.width, self.height);
}
}
fn main() {
let components: Vec<Box<dyn Draw>> = vec![ // Box<dyn Trait> 动态分发,运行时多态
Box::new(Button { width: 100, height: 50 }),
];
for component in components {
component.draw();
}
}
🔗 Rc¶
3.1 Rc 基础¶
Rust
use std::rc::Rc;
fn main() {
let a = Rc::new(5);
let b = Rc::clone(&a);
let c = Rc::clone(&a);
println!("count after creating c: {}", Rc::strong_count(&a));
}
3.2 Rc 的使用场景¶
Rust
use std::rc::Rc;
enum List {
Cons(i32, Rc<List>),
Nil,
}
use List::{Cons, Nil};
fn main() {
let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil)))));
let b = Cons(3, Rc::clone(&a));
let c = Cons(4, Rc::clone(&a));
println!("count after creating b: {}", Rc::strong_count(&a));
println!("count after creating c: {}", Rc::strong_count(&a));
}
⚡ Arc¶
4.1 Arc 基础¶
Rust
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..5 {
let data = Arc::clone(&data);
let handle = thread::spawn(move || {
println!("Thread {}: {:?}", i, data);
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
}
4.2 Arc 的使用场景¶
Rust
use std::sync::{Arc, Mutex};
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());
}
🔓 RefCell¶
5.1 RefCell 基础¶
Rust
use std::cell::RefCell;
fn main() {
let data = RefCell::new(5);
// 获取可变引用
*data.borrow_mut() += 1;
// 获取不可变引用
println!("data: {}", data.borrow());
}
5.2 内部可变性¶
Rust
pub trait Messenger {
fn send(&self, msg: &str);
}
pub struct LimitTracker<'a, T: Messenger> {
messenger: &'a T,
value: usize,
max: usize,
}
impl<'a, T> LimitTracker<'a, T>
where
T: Messenger,
{
pub fn new(messenger: &'a T, max: usize) -> LimitTracker<'a, T> {
LimitTracker {
messenger,
value: 0,
max,
}
}
pub fn set_value(&mut self, value: usize) {
self.value = value;
let percentage = self.value as f64 / self.max as f64;
if percentage >= 1.0 {
self.messenger.send("Error: You are over your quota!");
} else if percentage >= 0.9 {
self.messenger.send("Urgent warning: You've used 90% of your quota!");
} else if percentage >= 0.75 {
self.messenger.send("Warning: You've used 75% of your quota!");
}
}
}
5.3 Rc¶
Rust
use std::cell::RefCell;
use std::rc::Rc;
#[derive(Debug)]
enum List {
Cons(Rc<RefCell<i32>>, Rc<List>),
Nil,
}
use List::{Cons, Nil};
fn main() {
let value = Rc::new(RefCell::new(5));
let a = Rc::new(Cons(Rc::clone(&value), Rc::new(Nil)));
let b = Cons(Rc::new(RefCell::new(3)), Rc::clone(&a));
let c = Cons(Rc::new(RefCell::new(4)), Rc::clone(&a));
// Rc自动解引用到RefCell -> borrow_mut()获取可变借用RefMut<i32> -> *解引用修改内部值
*value.borrow_mut() += 10;
println!("a after = {:?}", a);
println!("b after = {:?}", b);
println!("c after = {:?}", c);
}
🔗 Weak¶
6.1 避免循环引用¶
Rust
use std::cell::RefCell;
use std::rc::{Rc, Weak};
#[derive(Debug)]
struct Node {
value: i32,
children: RefCell<Vec<Rc<Node>>>,
parent: RefCell<Weak<Node>>,
}
fn main() {
let leaf = Rc::new(Node {
value: 3,
children: RefCell::new(vec![]),
parent: RefCell::new(Weak::new()),
});
println!("leaf parent = {:?}", leaf.parent.borrow().upgrade());
let branch = Rc::new(Node {
value: 5,
children: RefCell::new(vec![Rc::clone(&leaf)]),
parent: RefCell::new(Weak::new()),
});
*leaf.parent.borrow_mut() = Rc::downgrade(&branch);
println!("leaf parent = {:?}", leaf.parent.borrow().upgrade());
}
📝 练习题¶
练习 1:Box 使用¶
Rust
// TODO: 使用 Box 实现一个二叉树
// 包含 value, left, right 三个字段
enum BinaryTree {
// TODO: 定义变体
}
fn main() {
// TODO: 创建一个简单的二叉树并打印
}
练习 2:Rc 使用¶
Rust
use std::rc::Rc;
// TODO: 使用 Rc 实现一个图结构
// 多个节点可以共享同一个子节点
struct Node {
// TODO: 定义字段
}
fn main() {
// TODO: 创建图结构并测试引用计数
}
练习 3:RefCell 使用¶
Rust
use std::cell::RefCell;
// TODO: 实现一个简单的缓存结构
// 使用 RefCell 实现内部可变性
struct Cache<T> {
// TODO: 定义字段
}
impl<T: Clone> Cache<T> {
// TODO: 实现方法
}
fn main() {
// TODO: 测试缓存
}
练习 4:Weak 使用¶
Rust
use std::cell::RefCell;
use std::rc::{Rc, Weak};
// TODO: 实现一个双向链表
// 使用 Weak 避免循环引用
struct Node {
// TODO: 定义字段
}
fn main() {
// TODO: 创建双向链表并测试
}
💡 最佳实践¶
1. 选择合适的智能指针¶
Rust
// Box: 单一所有权,堆上分配
let boxed = Box::new(5);
// Rc: 多重所有权,单线程
let shared = Rc::new(5);
// Arc: 多重所有权,多线程
use std::sync::Arc;
let shared = Arc::new(5);
// RefCell: 内部可变性,单线程
use std::cell::RefCell;
let mutable = RefCell::new(5);
2. 避免循环引用¶
Rust
// 好:使用 Weak 避免循环引用
use std::rc::{Rc, Weak};
let weak = Rc::downgrade(&rc);
// 避免:直接使用 Rc 创建循环引用
3. 使用 Rc::clone 而不是 clone¶
4. 检查引用计数¶
⚠️ 常见错误¶
1. 运行时借用检查失败¶
Rust
// 错误:运行时 panic
use std::cell::RefCell;
let data = RefCell::new(5);
let ref1 = data.borrow();
let ref2 = data.borrow_mut(); // panic!
// 正确:释放引用后再借用
use std::cell::RefCell;
let data = RefCell::new(5);
{
let ref1 = data.borrow();
println!("{}", ref1);
}
let ref2 = data.borrow_mut();
*ref2 += 1;
2. 循环引用导致内存泄漏¶
Rust
// 错误:循环引用
use std::rc::Rc;
use std::cell::RefCell;
struct Node {
next: RefCell<Option<Rc<Node>>>,
}
let a = Rc::new(Node { next: RefCell::new(None) });
let b = Rc::new(Node { next: RefCell::new(Some(Rc::clone(&a))) });
*a.next.borrow_mut() = Some(Rc::clone(&b));
// 内存泄漏!a 和 b 的引用计数永远不会为 0
3. 在多线程中使用 Rc¶
Rust
// 错误:Rc 不是线程安全的
use std::rc::Rc;
use std::thread;
let data = Rc::new(5);
thread::spawn(move || {
println!("{}", data); // 编译错误
});
// 正确:使用 Arc
use std::sync::Arc;
let data = Arc::new(5);
thread::spawn(move || {
println!("{}", data);
});
📚 扩展阅读¶
🎯 本章小结¶
本章介绍了 Rust 的智能指针:
- ✅ 理解智能指针的概念
- ✅ 掌握 Box 的使用场景
- ✅ 学会使用 Rc 实现多重所有权
- ✅ 理解 Arc 的线程安全特性
- ✅ 掌握 RefCell 的内部可变性
- ✅ 学会使用 Weak 避免循环引用
下一章: 我们将学习 Rust 的宏编程,包括声明宏、过程宏和宏规则。