强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Rust 系统编程语言完全教程 / 第15章:智能指针

第15章:智能指针

15.1 什么是智能指针

智能指针(smart pointer)是一种数据结构,表现类似指针,但拥有额外的元数据和功能。

类型所有权借用检查线程安全用途
Box<T>独占编译时堆分配
Rc<T>共享编译时单线程共享
Arc<T>共享编译时多线程共享
RefCell<T>独占运行时内部可变性
Cell<T>独占无(Copy)内部可变性

15.2 Box

在堆上分配数据。

基本用法

fn main() {
    // 在堆上分配一个 i32
    let b = Box::new(5);
    println!("b = {}", b); // 自动解引用

    // 使用场景1:递归类型
    // 使用场景2:大数据避免栈溢出
    // 使用场景3:trait 对象
}

递归类型

// 链表:每个节点包含值和指向下一个节点的指针
#[derive(Debug)]
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))))));
    println!("{:?}", list);

    // 打印链表
    fn print_list(list: &List) {
        match list {
            Cons(val, next) => {
                print!("{} -> ", val);
                print_list(next);
            }
            Nil => println!("Nil"),
        }
    }

    print_list(&list);
}

Box 大小

fn main() {
    // Box<T> 的大小是一个指针大小(8 字节在 64 位系统上)
    println!("Box<i32> 大小: {} 字节", std::mem::size_of::<Box<i32>>());   // 8
    println!("Box<[i32; 1000]> 大小: {} 字节", std::mem::size_of::<Box<[i32; 1000]>>()); // 8
    println!("[i32; 1000] 大小: {} 字节", std::mem::size_of::<[i32; 1000]>()); // 4000
}

15.3 Deref Trait

自定义 Deref

use std::ops::Deref;

struct MyBox<T>(T);

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

impl<T> Deref for MyBox<T> {
    type Target = T;

    fn deref(&self) -> &T {
        &self.0
    }
}

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

fn main() {
    let x = 5;
    let y = MyBox::new(x);

    assert_eq!(5, x);
    assert_eq!(5, *y); // 通过 Deref 解引用

    // Deref 强制转换
    let m = MyBox::new(String::from("Rust"));
    hello(&m); // MyBox<String> → &String → &str
    // 等价于 hello(&(*m)[..]);
}

Deref 强制转换规则

  1. T: Deref<Target=U> 时,&T 自动转换为 &U
  2. T: DerefMut<Target=U> 时,&mut T 自动转换为 &mut U

这就是为什么 &String 可以用在需要 &str 的地方。


15.4 Drop Trait

自定义清理逻辑

struct DatabaseConnection {
    url: String,
}

impl DatabaseConnection {
    fn new(url: &str) -> Self {
        println!("连接到数据库: {}", url);
        Self { url: url.to_string() }
    }

    fn query(&self, sql: &str) {
        println!("执行查询: {}", sql);
    }
}

impl Drop for DatabaseConnection {
    fn drop(&mut self) {
        println!("关闭数据库连接: {}", self.url);
    }
}

fn main() {
    let conn = DatabaseConnection::new("postgres://localhost/mydb");
    conn.query("SELECT * FROM users");

    // 手动提前释放
    let conn2 = DatabaseConnection::new("postgres://localhost/other");
    drop(conn2); // 显式调用 drop
    println!("conn2 已释放");

    // conn 在 main 结束时自动释放
    println!("函数即将结束");
}

15.5 Rc:引用计数

基本用法

use std::rc::Rc;

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

    // 克隆 Rc 增加引用计数,不复制数据
    let b = Rc::clone(&a);
    let c = Rc::clone(&a);

    println!("a = {:?}", a);
    println!("b = {:?}", b);
    println!("引用计数: {}", Rc::strong_count(&a)); // 3

    drop(b);
    println!("drop b 后引用计数: {}", Rc::strong_count(&a)); // 2
}

共享链表

use std::rc::Rc;

#[derive(Debug)]
enum List {
    Cons(i32, Rc<List>),
    Nil,
}

fn main() {
    let shared = Rc::new(List::Cons(5, Rc::new(List::Nil)));

    // a 和 b 共享同一个链表尾部
    let a = List::Cons(10, Rc::clone(&shared));
    let b = List::Cons(20, Rc::clone(&shared));

    println!("a = {:?}", a);
    println!("b = {:?}", b);
    println!("shared 引用计数: {}", Rc::strong_count(&shared)); // 3
}

注意: Rc<T> 仅适用于单线程。多线程请使用 Arc<T>


15.6 Arc:原子引用计数

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 = Arc::clone(&data);
        let handle = thread::spawn(move || {
            let sum: i32 = data.iter().sum();
            println!("线程 {}: sum = {}", i, sum);
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }

    println!("引用计数: {}", Arc::strong_count(&data));
}

Rc vs Arc

特性RcArc
线程安全
性能更快(非原子操作)较慢(原子操作)
适用场景单线程共享多线程共享
Send + Sync

15.7 RefCell:内部可变性

运行时借用检查

use std::cell::RefCell;

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

    // 不可变借用
    {
        let borrowed = data.borrow();
        println!("数据: {:?}", borrowed);
    } // borrowed 在此释放

    // 可变借用
    {
        let mut borrowed = data.borrow_mut();
        borrowed.push(4);
        borrowed.push(5);
    } // borrowed 在此释放

    println!("修改后: {:?}", data.borrow());
}

运行时 panic

use std::cell::RefCell;

fn main() {
    let data = RefCell::new(42);

    let borrow1 = data.borrow();
    // let borrow2 = data.borrow_mut(); // ❌ 运行时 panic: already borrowed

    println!("{}", borrow1);
    drop(borrow1);

    let mut borrow2 = data.borrow_mut();
    *borrow2 = 100;
    drop(borrow2);

    println!("{}", data.borrow());
}

Rc<RefCell>:共享可变状态

use std::cell::RefCell;
use std::rc::Rc;

#[derive(Debug)]
struct SharedState {
    value: i32,
    history: Vec<i32>,
}

impl SharedState {
    fn new(value: i32) -> Rc<RefCell<Self>> {
        Rc::new(RefCell::new(Self {
            value,
            history: vec![value],
        }))
    }

    fn update(state: &Rc<RefCell<Self>>, new_value: i32) {
        let mut s = state.borrow_mut();
        s.value = new_value;
        s.history.push(new_value);
    }
}

fn main() {
    let state = SharedState::new(0);

    // 多处共享同一状态
    let observer = Rc::clone(&state);

    SharedState::update(&state, 10);
    SharedState::update(&state, 20);
    SharedState::update(&state, 30);

    // observer 可以看到所有修改
    let s = observer.borrow();
    println!("当前值: {}", s.value);
    println!("历史: {:?}", s.history);
}

15.8 Cell

use std::cell::Cell;

fn main() {
    // Cell 适用于 Copy 类型
    let c = Cell::new(42);
    c.set(100);
    println!("Cell: {}", c.get()); // 100

    // Cell 不需要借用检查,性能更好
    let counter = Cell::new(0);
    for _ in 0..10 {
        counter.set(counter.get() + 1);
    }
    println!("计数: {}", counter.get()); // 10
}

Cell vs RefCell

特性CellRefCell
类型限制T: Copy任意 T
借用检查无(直接替换)运行时
性能更快较慢
获取值.get() (Copy).borrow() (引用)
设置值.set().borrow_mut()

15.9 Weak:弱引用

打破循环引用。

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

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

fn main() {
    let leaf = Rc::new(Node {
        value: 3,
        parent: RefCell::new(Weak::new()),
        children: RefCell::new(vec![]),
    });

    let branch = Rc::new(Node {
        value: 5,
        parent: RefCell::new(Weak::new()),
        children: RefCell::new(vec![Rc::clone(&leaf)]),
    });

    // 设置 leaf 的 parent 为 branch 的弱引用
    *leaf.parent.borrow_mut() = Rc::downgrade(&branch);

    // 通过弱引用访问 parent
    if let Some(parent) = leaf.parent.borrow().upgrade() {
        println!("leaf 的 parent: {}", parent.value);
    }

    println!("branch 强引用数: {}", Rc::strong_count(&branch)); // 1
    println!("branch 弱引用数: {}", Rc::weak_count(&branch));   // 1
}

15.10 业务场景示例

共享配置

use std::sync::Arc;
use std::collections::HashMap;

#[derive(Debug, Clone)]
struct AppConfig {
    database_url: String,
    max_connections: u32,
    debug: bool,
}

struct AppState {
    config: Arc<AppConfig>,
    request_count: std::sync::atomic::AtomicU64,
}

impl AppState {
    fn new(config: AppConfig) -> Self {
        Self {
            config: Arc::new(config),
            request_count: std::sync::atomic::AtomicU64::new(0),
        }
    }

    fn handle_request(&self) {
        self.request_count.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
        let count = self.request_count.load(std::sync::atomic::Ordering::Relaxed);

        if self.config.debug {
            println!("请求 #{} - DB: {}", count, self.config.database_url);
        }
    }
}

fn main() {
    let config = AppConfig {
        database_url: "postgres://localhost/mydb".to_string(),
        max_connections: 10,
        debug: true,
    };

    let state = Arc::new(AppState::new(config));

    let handles: Vec<_> = (0..5)
        .map(|_| {
            let state = Arc::clone(&state);
            std::thread::spawn(move || {
                state.handle_request();
            })
        })
        .collect();

    for handle in handles {
        handle.join().unwrap();
    }
}

观察者模式

use std::cell::RefCell;
use std::rc::Rc;

type Observer = Rc<RefCell<dyn FnMut(i32)>>;

struct EventEmitter {
    observers: Vec<Observer>,
}

impl EventEmitter {
    fn new() -> Self {
        Self { observers: Vec::new() }
    }

    fn subscribe(&mut self, observer: Observer) {
        self.observers.push(observer);
    }

    fn emit(&self, value: i32) {
        for observer in &self.observers {
            (*observer.borrow_mut())(value);
        }
    }
}

fn main() {
    let mut emitter = EventEmitter::new();

    let log: Rc<RefCell<Vec<i32>>> = Rc::new(RefCell::new(Vec::new()));
    let log_clone = Rc::clone(&log);

    emitter.subscribe(Rc::new(RefCell::new(move |val| {
        println!("观察者1: 收到 {}", val);
    })));

    emitter.subscribe(Rc::new(RefCell::new(move |val| {
        log_clone.borrow_mut().push(val);
    })));

    emitter.emit(10);
    emitter.emit(20);
    emitter.emit(30);

    println!("日志: {:?}", log.borrow());
}

15.11 本章小结

要点说明
Box堆分配,独占所有权
Rc单线程共享,引用计数
Arc多线程共享,原子引用计数
RefCell内部可变性,运行时借用检查
Cell内部可变性,Copy 类型专用
Weak弱引用,不增加引用计数
Deref自定义解引用行为
Drop自定义清理逻辑

扩展阅读

  1. Rust Book - 智能指针 — 官方教程
  2. Rc 文档 — Rc API 参考
  3. Arc 文档 — Arc API 参考