核心特性

第14课:智能指针

🎯 本课目标

掌握Box、Rc、Arc、RefCell等智能指针,理解所有权与借用的运行时扩展。

1. Box<T> — 堆分配

fn main() {
    // 在堆上分配
    let b = Box::new(5);
    println!("b = {}", b);

    // 递归类型必须用Box
    enum List {
        Cons(i32, Box<List>),
        Nil,
    }
    use List::*;
    let list = Cons(1, Box::new(Cons(2, Box::new(Nil))));

    // 动态大小类型
    fn process(s: Box<dyn std::fmt::Display>) {
        println!("{}", s);
    }
    process(Box::new(42));
    process(Box::new("hello"));
}

✅ 编译验证通过

2. Deref与DerefMut

use std::ops::Deref;

struct MyBox<T>(T>;

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

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

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

fn main() {
    let m = MyBox::new(String::from("Rust"));
    hello(&m);  // Deref强制转换: &MyBox<String> → &String → &str
}

✅ 编译验证通过

3. Drop trait

struct CustomPointer {
    data: String,
}

impl Drop for CustomPointer {
    fn drop(&mut self) {
        println!("释放: {}", self.data);
    }
}

fn main() {
    let c = CustomPointer { data: String::from("资源A") };
    println!("使用中...");
    // c.drop();  // ❌ 不允许显式调用
    drop(c);      // ✅ std::mem::drop
    println!("已释放");
}

✅ 编译验证通过

4. Rc<T> — 引用计数

use std::rc::Rc;

fn main() {
    let a = Rc::new(String::from("共享数据"));
    let b = Rc::clone(&a);  // 增加引用计数,不复制数据!
    let c = Rc::clone(&a);

    println!("引用计数: {}", Rc::strong_count(&a));
    println!("值: {}", *a);
    // Rc只能用于单线程!
}

✅ 编译验证通过

5. RefCell<T> — 内部可变性

use std::cell::RefCell;

fn main() {
    let data = RefCell::new(vec![1, 2, 3]);

    // 借用检查移到运行时
    data.borrow_mut().push(4);  // 可变借用
    println!("{:?}", data.borrow());  // 不可变借用

    // ⚠️ 运行时借用冲突会panic!
    // let ref1 = data.borrow();
    // let ref2 = data.borrow_mut();  // 💥 panic!
}

✅ 编译验证通过

6. Arc<T> — 原子引用计数

use std::sync::Arc;
use std::thread;

fn main() {
    let data = Arc::new(vec![1, 2, 3]);
    let mut handles = vec![];

    for _ in 0..3 {
        let data_clone = Arc::clone(&data);
        handles.push(thread::spawn(move || {
            println!("线程读取: {:?}", data_clone);
        }));
    }
    for h in handles { h.join().unwrap(); }
}

✅ 编译验证通过

7. 智能指针对比

类型所有权线程安全可变性开销
Box<T>唯一编译期
Rc<T>共享编译期引用计数
Arc<T>共享编译期原子计数
RefCell<T>唯一运行时运行时检查
Mutex<T>共享运行时
智能指针内存模型: Box<i32>: 栈 堆 ┌────────┐ ┌─────┐ │ ptr ──────────→│ 42 │ │ │ └─────┘ └────────┘ Rc<String>: 栈1 ──┐ 堆 栈2 ──┼→ RefCnt → String("hello") 栈3 ──┘ count=3 RefCell<Vec>: ┌─────────────────┐ │ borrow_state │ 运行时追踪 │ data: Vec │ └─────────────────┘

📝 练习题

练习1:双向链表

用Rc+RefCell实现双向链表。

练习2:DAG图

用Rc实现有向无环图。

练习3:自定义智能指针

实现一个简单的引用计数智能指针。

🏆

成就解锁:指针巫师

掌握智能指针——突破编译期限制的钥匙


📚 扩展阅读

以下资源帮助你深入学习本课主题:

🔑 关键术语回顾

本课涉及的核心概念,确保你理解每一个:

术语说明
所有权Rust内存管理的核心机制,每个值有唯一所有者
借用通过引用访问数据,不获取所有权
生命周期引用有效的范围,编译期分析工具
traitRust的接口/抽象机制,类似其他语言的接口
泛型参数化类型,零成本抽象
模式匹配强大的数据解构和分支机制
零成本抽象高层抽象不引入运行时开销
fearless concurrency编译器保证线程安全

💬 学习建议

  1. 动手编码 — 每个代码示例都在本地运行一遍
  2. 修改实验 — 故意改错代码,看编译器报什么错
  3. 完成练习 — 每课的练习题是巩固知识的关键
  4. 阅读源码 — 看标准库和优秀开源项目的实现
  5. 写项目 — 真正掌握需要构建真实项目

🤔 常见问题

Q: Rust学习曲线真的很陡吗?

A: 前期确实需要适应所有权和借用检查器,但一旦理解了,这些概念会让你的代码更可靠。大多数人2-4周就能上手。

Q: Rust适合什么项目?

A: 系统编程、Web服务、CLI工具、嵌入式、WASM、网络服务、数据库等。基本上需要性能和安全的地方都适合。

Q: 遇到编译错误怎么办?

A: Rust编译器的错误信息非常友好!仔细阅读,通常会指出问题所在和修复建议。也可以用cargo clippy获取更多提示。