🦀 第12课:智能指针(Box/Rc/Arc)

智能指针是拥有数据所有权的指针,除了指向数据外,还提供额外的功能。Rust标准库提供了多种智能指针,最常用的有Box<T>Rc<T>Arc<T>RefCell<T>

进阶特性 第12/25课

学习目标:掌握Box堆分配、Rc共享所有权、Arc线程安全共享、RefCell内部可变性、Deref/Drop特征

📦 Box<T> —— 堆分配

fn main() {
    // 基本用法:将数据放到堆上
    let b = Box::new(5);
    println!("b = {}", b);
    
    // 场景1:编译时大小未知的类型
    enum List {
        Cons(i32, Box<List>),
        Nil,
    }
    use List::{Cons, Nil};
    
    let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
    println!("链表创建成功");
    
    // 遍历链表
    fn sum_list(list: &List) -> i32 {
        match list {
            Cons(val, next) => val + sum_list(next),
            Nil => 0,
        }
    }
    println!("链表和: {}", sum_list(&list));
    
    // 场景2:递归类型大小
    println!("i32大小: {} 字节", std::mem::size_of::());
    println!("Box大小: {} 字节", std::mem::size_of::>());
    
    // 场景3:trait对象
    trait Animal {
        fn speak(&self) -> String;
    }
    struct Dog;
    struct Cat;
    impl Animal for Dog { fn speak(&self) -> String { "汪汪!".into() } }
    impl Animal for Cat { fn speak(&self) -> String { "喵喵!".into() } }
    
    let animals: Vec> = vec![
        Box::new(Dog),
        Box::new(Cat),
    ];
    for a in &animals {
        println!("{}", a.speak());
    }
}
b = 5 链表创建成功 链表和: 6 i32大小: 4 字节 Box大小: 8 字节 汪汪! 喵喵!

✅ 验证通过

🔄 Rc<T> —— 引用计数共享所有权

当需要多个所有者共享同一数据时,使用Rc(Reference Counted):

use std::rc::Rc;

fn main() {
    // Rc允许多个所有者
    let data = Rc::new(vec![1, 2, 3, 4, 5]);
    
    let d1 = Rc::clone(&data);  // 增加引用计数,不复制数据
    let d2 = Rc::clone(&data);
    
    println!("引用计数: {}", Rc::strong_count(&data));  // 3
    println!("d1: {:?}", *d1);
    println!("d2: {:?}", *d2);
    
    // 共享图结构
    #[derive(Debug)]
    enum Node {
        Leaf(i32),
        Branch {
            value: i32,
            children: Vec<Rc<Node>>,
        },
    }
    
    let leaf1 = Rc::new(Node::Leaf(1));
    let leaf2 = Rc::new(Node::Leaf(2));
    let leaf3 = Rc::new(Node::Leaf(3));
    
    // 两个分支共享leaf2
    let branch1 = Rc::new(Node::Branch {
        value: 10,
        children: vec![Rc::clone(&leaf1), Rc::clone(&leaf2)],
    });
    
    let branch2 = Rc::new(Node::Branch {
        value: 20,
        children: vec![Rc::clone(&leaf2), Rc::clone(&leaf3)],
    });
    
    println!("branch1: {:?}", branch1);
    println!("branch2: {:?}", branch2);
    println!("leaf2引用计数: {}", Rc::strong_count(&leaf2));  // 3
    
    drop(branch1);  // branch1被drop
    println!("leaf2引用计数(drop后): {}", Rc::strong_count(&leaf2));  // 2
}
引用计数: 3 d1: [1, 2, 3, 4, 5] d2: [1, 2, 3, 4, 5] branch1: Branch { value: 10, children: [Leaf(1), Leaf(2)] } branch2: Branch { value: 20, children: [Leaf(2), Leaf(3)] } leaf2引用计数: 3 leaf2引用计数(drop后): 2

✅ 验证通过

⚠️ Rc不是线程安全的!不能跨线程使用Rc。跨线程共享请用Arc<T>

🧵 Arc<T> —— 原子引用计数

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_clone = Arc::clone(&data);
        let handle = thread::spawn(move || {
            println!("线程{}: 数据={:?}, 引用计数={}", 
                     i, *data_clone, Arc::strong_count(&data_clone));
        });
        handles.push(handle);
    }
    
    for handle in handles {
        handle.join().unwrap();
    }
    
    println!("主线程引用计数: {}", Arc::strong_count(&data));
}
线程0: 数据=[1, 2, 3, 4, 5], 引用计数=2 线程2: 数据=[1, 2, 3, 4, 5], 引用计数=3 线程1: 数据=[1, 2, 3, 4, 5], 引用计数=4 主线程引用计数: 1

✅ 验证通过

🔓 RefCell<T> —— 内部可变性

有时你需要在对不可变引用的情况下修改数据。RefCell将借用检查从编译期推迟到运行期:

use std::cell::RefCell;

fn main() {
    // RefCell允许在&self方法中修改内部数据
    let data = RefCell::new(vec![1, 2, 3]);
    
    // borrow() → 不可变借用(运行时检查)
    // borrow_mut() → 可变借用(运行时检查)
    
    data.borrow_mut().push(4);  // 即使data不是mut!
    println!("修改后: {:?}", data.borrow());
    
    // 实际应用:模拟消息记录器
    trait Messenger {
        fn send(&self, msg: &str);  // 注意: &self不是&mut self
    }
    
    struct MockMessenger {
        sent_messages: RefCell>,
    }
    
    impl MockMessenger {
        fn new() -> Self {
            MockMessenger {
                sent_messages: RefCell::new(Vec::new()),
            }
        }
    }
    
    impl Messenger for MockMessenger {
        fn send(&self, msg: &str) {
            // 通过RefCell在&self中修改数据!
            self.sent_messages.borrow_mut().push(msg.to_string());
        }
    }
    
    let messenger = MockMessenger::new();
    messenger.send("Hello");
    messenger.send("World");
    println!("发送的消息: {:?}", messenger.sent_messages.borrow());
    
    // ⚠️ 运行时借用规则仍然有效!
    // let ref1 = data.borrow_mut();
    // let ref2 = data.borrow_mut();  // ❌ 运行时panic! 已有可变借用
    
    // 安全的多次借用
    {
        let r1 = data.borrow();     // 不可变借用 ✅
        let r2 = data.borrow();     // 再来一个 ✅
        println!("r1={:?}, r2={:?}", r1, r2);
    }  // r1, r2离开作用域
    
    {
        let mut w = data.borrow_mut();  // 可变借用 ✅
        w.push(5);
        println!("写入后: {:?}", w);
    }
}
修改后: [1, 2, 3, 4] 发送的消息: ["Hello", "World"] r1=[1, 2, 3, 4], r2=[1, 2, 3, 4] 写入后: [1, 2, 3, 4, 5]

✅ 验证通过

🔀 Deref和Drop特征

use std::ops::Deref;

// 自定义智能指针
struct MyBox(T);

impl MyBox {
    fn new(x: T) -> Self {
        MyBox(x)
    }
}

impl Deref for MyBox {
    type Target = T;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl Drop for MyBox {
    fn drop(&mut self) {
        println!("MyBox被drop! 值被释放");
    }
}

fn hello(name: &str) {
    println!("你好, {}!", name);
}

fn main() {
    let x = MyBox::new(5);
    println!("x = {}", *x);  // Deref强制转换
    
    let name = MyBox::new(String::from("Rust"));
    hello(&name);  // Deref: &MyBox → &String → &str
    
    // Drop自动调用
    {
        let _b = MyBox::new("临时值");
        println!("作用域内");
    }  // Drop在这里自动调用
    println!("作用域外");
}
x = 5 你好, Rust! 作用域内 MyBox被drop! 值被释放 作用域外

✅ 验证通过

🏗️ 综合实战:多视图图结构

use std::rc::{Rc, Weak};
use std::cell::RefCell;

#[derive(Debug)]
struct Node {
    value: i32,
    children: RefCell>>,
    parent: RefCell>>,  // Weak避免循环引用
}

impl Node {
    fn new(value: i32) -> Rc {
        Rc::new(Node {
            value,
            children: RefCell::new(Vec::new()),
            parent: RefCell::new(None),
        })
    }
    
    fn add_child(parent: &Rc, child: Rc) {
        *child.parent.borrow_mut() = Some(Rc::downgrade(parent));
        parent.children.borrow_mut().push(child);
    }
    
    fn print_tree(&self, depth: usize) {
        println!("{}{}", "  ".repeat(depth), self.value);
        for child in self.children.borrow().iter() {
            child.print_tree(depth + 1);
        }
    }
}

fn main() {
    let root = Node::new(1);
    let child1 = Node::new(2);
    let child2 = Node::new(3);
    let grandchild = Node::new(4);
    
    Node::add_child(&root, child1);
    Node::add_child(&root, child2);
    Node::add_child(&root.children.borrow()[0].clone(), grandchild);
    
    println!("🌲 树结构:");
    root.print_tree(0);
    
    // 从子节点访问父节点
    if let Some(parent) = root.children.borrow()[0].parent.borrow().as_ref() {
        if let Some(p) = parent.upgrade() {
            println!("\n节点2的父节点: {}", p.value);
        }
    }
    
    // Weak引用不会增加strong_count
    println!("root引用计数: {}", Rc::strong_count(&root));
}
🌲 树结构: 1 2 4 3 节点2的父节点: 1 root引用计数: 1

✅ 验证通过

📝 练习

练习1:二叉搜索树

Box实现二叉搜索树,支持insert、search、inorder遍历。

练习2:双向链表

Rc<RefCell<T>>Weak<T>实现双向链表,支持push/pop/遍历。

练习3:缓存系统

Rc<RefCell<HashMap>>实现共享可变缓存,多个模块可以读写同一缓存。

🏆 本课成就

🔒 下一课解锁:闭包与迭代器 —— Rust的函数式编程

🔧 智能指针选择指南

场景选择原因
递归类型/不确定大小Box<T>固定大小指针
多所有者(单线程)Rc<T>引用计数
多所有者(多线程)Arc<T>原子引用计数
内部可变性(单线程)RefCell<T>运行时借用检查
内部可变性(多线程)Mutex<T>锁保护
共享可变状态Rc<RefCell<T>>引用计数+内部可变
线程安全共享可变Arc<Mutex<T>>最常用的并发模式
弱引用(防循环)Weak<T>不增加引用计数
use std::rc::{Rc, Weak};
use std::cell::RefCell;

fn main() {
    // Rc> 模式
    let shared_list = Rc::new(RefCell::new(vec![1, 2, 3]));
    
    let list1 = Rc::clone(&shared_list);
    list1.borrow_mut().push(4);
    
    let list2 = Rc::clone(&shared_list);
    list2.borrow_mut().push(5);
    
    println!("共享列表: {:?}", shared_list.borrow());
    println!("引用计数: {}", Rc::strong_count(&shared_list));
    
    // Weak引用防止循环
    let strong = Rc::new(42);
    let weak: Weak = Rc::downgrade(&strong);
    
    println!("强引用: {}, 弱引用: {}", Rc::strong_count(&strong), Rc::weak_count(&strong));
    println!("弱引用升级: {:?}", weak.upgrade());
    
    drop(strong);
    println!("释放后弱引用: {:?}", weak.upgrade());  // None
}
共享列表: [1, 2, 3, 4, 5] 引用计数: 3 强引用: 1, 弱引用: 1 弱引用升级: Some(42) 释放后弱引用: None

✅ 验证通过