完整示例
示例 1:缓存系统
rust
▶ Runuse std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
struct Cache {
data: RefCell<HashMap<String, Rc<String>>>,
}
impl Cache {
fn new() -> Self {
Cache {
data: RefCell::new(HashMap::new()),
}
}
fn get_or_insert(&self, key: &str, value: String) -> Rc<String> {
if let Some(rc) = self.data.borrow().get(key) {
Rc::clone(rc)
} else {
let rc = Rc::new(value);
self.data.borrow_mut().insert(key.to_string(), Rc::clone(&rc));
rc
}
}
fn len(&self) -> usize {
self.data.borrow().len()
}
}
fn main() {
let cache = Cache::new();
let v1 = cache.get_or_insert("key1", String::from("value1"));
let v2 = cache.get_or_insert("key1", String::from("value1"));
let v3 = cache.get_or_insert("key2", String::from("value2"));
println!("缓存大小:{}", cache.len()); // 2
println!("v1 == v2: {}", Rc::ptr_eq(&v1, &v2)); // true
}示例 2:消息传递
rust
▶ Runuse std::cell::RefCell;
use std::rc::Rc;
struct Message {
content: Rc<RefCell<String>>,
}
impl Message {
fn new(content: &str) -> Self {
Message {
content: Rc::new(RefCell::new(content.to_string())),
}
}
fn edit(&self, new_content: &str) {
*self.content.borrow_mut() = new_content.to_string();
}
fn print(&self) {
println!("{}", self.content.borrow());
}
}
fn main() {
let msg = Message::new("Hello");
msg.print(); // Hello
msg.edit("World");
msg.print(); // World
}常见错误
错误 1:循环引用
rust
▶ Runuse std::cell::RefCell;
use std::rc::Rc;
struct Node {
value: i32,
// ❌ 错误:使用 Rc 会循环引用
// next: Rc<RefCell<Node>>,
// ✅ 正确:使用 Weak
next: Option<Rc<RefCell<Node>>>,
prev: Option<Weak<RefCell<Node>>>,
}错误 2:借用冲突
rust
▶ Runuse std::cell::RefCell;
fn main() {
let data = RefCell::new(5);
// ❌ 错误:同时存在多个可变借用
// let mut a = data.borrow_mut();
// let mut b = data.borrow_mut(); // panic!
// ✅ 正确:作用域分离
{
let mut a = data.borrow_mut();
*a += 1;
}
{
let mut b = data.borrow_mut();
*b += 1;
}
}错误 3:Weak 访问
rust
▶ Runuse std::rc::Weak;
// ❌ 错误:直接访问 Weak
// let weak: Weak<i32> = ...;
// println!("{}", *weak);
// ✅ 正确:先升级
if let Some(rc) = weak.upgrade() {
println!("{}", *rc);
}智能指针选择指南
┌─────────────────────────────────────────────────────┐
│ 智能指针选择指南 │
├─────────────────────────────────────────────────────┤
│ │
│ 需要堆上分配? │
│ ├── 是,递归类型/大对象 → Box<T> │
│ └── 否 → 继续 │
│ │
│ 需要多线程共享? │
│ ├── 是 → Arc<T> │
│ │ ├── 需要修改? → Arc<Mutex<T>> │
│ │ └── 只读? → Arc<T> │
│ └── 否 → 继续 │
│ │
│ 需要单线程共享? │
│ ├── 是 → Rc<T> │
│ │ ├── 需要修改? → Rc<RefCell<T>> │
│ │ └── 只读? → Rc<T> │
│ └── 否 → Box<T> 或普通引用 │
│ │
│ 需要打破循环引用? → Weak<T> │
│ │
└─────────────────────────────────────────────────────┘练习
练习 1:Box 实现栈
使用 Box 实现一个栈数据结构:
rust
▶ Runenum Stack<T> {
Empty,
Cons(T, Box<Stack<T>>),
}练习 2:共享计数器
使用 Rc<RefCell<T>> 实现一个共享计数器,多个所有者可以增加计数。
练习 3:多线程计数器
使用 Arc<Mutex<T>> 实现一个线程安全的计数器,多个线程同时增加。
小结
本章我们学习了:
- ✅ Box<T>:堆上分配
- ✅ Rc<T>:单线程引用计数
- ✅ RefCell<T>:内部可变性
- ✅ Arc<T>:多线程引用计数
- ✅ Weak<T>:弱引用
- ✅ 智能指针的组合使用
智能指针特性对比
| 类型 | 所有权 | 可变性 | 线程安全 | 开销 |
|---|---|---|---|---|
| Box<T> | 单一 | 可 mut | - | 无 |
| Rc<T> | 共享 | 不可变 | 否 | 无锁计数 |
| Arc<T> | 共享 | 不可变 | 是 | 原子计数 |
| RefCell<T> | 单一 | 运行时 | - | 运行时检查 |
| Arc<Mutex<T>> | 共享 | 可锁 | 是 | 锁开销 |