Skip to content

完整示例

示例 1:缓存系统

rust
use 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
}
▶ Run

示例 2:消息传递

rust
use 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
}
▶ Run

常见错误

错误 1:循环引用

rust
use 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>>>,
}
▶ Run

错误 2:借用冲突

rust
use 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;
    }
}
▶ Run

错误 3:Weak 访问

rust
use std::rc::Weak;

// ❌ 错误:直接访问 Weak
// let weak: Weak<i32> = ...;
// println!("{}", *weak);

// ✅ 正确:先升级
if let Some(rc) = weak.upgrade() {
    println!("{}", *rc);
}
▶ Run

智能指针选择指南

┌─────────────────────────────────────────────────────┐
│              智能指针选择指南                        │
├─────────────────────────────────────────────────────┤
│                                                     │
│  需要堆上分配?                                      │
│  ├── 是,递归类型/大对象 → Box<T>                   │
│  └── 否 → 继续                                      │
│                                                     │
│  需要多线程共享?                                    │
│  ├── 是 → Arc<T>                                    │
│  │           ├── 需要修改? → Arc<Mutex<T>>         │
│  │           └── 只读? → Arc<T>                    │
│  └── 否 → 继续                                      │
│                                                     │
│  需要单线程共享?                                    │
│  ├── 是 → Rc<T>                                     │
│  │           ├── 需要修改? → Rc<RefCell<T>>        │
│  │           └── 只读? → Rc<T>                     │
│  └── 否 → Box<T> 或普通引用                         │
│                                                     │
│  需要打破循环引用? → Weak<T>                       │
│                                                     │
└─────────────────────────────────────────────────────┘

练习

练习 1:Box 实现栈

使用 Box 实现一个栈数据结构:

rust
enum Stack<T> {
    Empty,
    Cons(T, Box<Stack<T>>),
}
▶ Run

练习 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>>共享可锁锁开销

第 23 章:并发编程