目录

📝 摘要

一、智能指针概述

1.1 什么是智能指针?

二、Box:堆上的数据

2.1 基础用法

2.2 递归类型

2.3 trait 对象

三、Rc:单线程引用计数

3.1 共享所有权

3.2 共享数据结构

3.3 弱引用(Weak)

四、Arc:线程安全的引用计数

4.1 多线程共享数据

4.2 Arc + Mutex 组合

4.3 性能考量

五、RefCell:内部可变性

5.1 运行时借用检查

5.2 Rc + RefCell 组合

5.3 实战:可变计数器

六、Cell:简单内部可变性

七、性能对比与选择指南

7.1 性能基准测试

7.2 选择决策树

八、实战案例

8.1 案例1:图数据结构

8.2 案例2:线程池

九、总结与讨论

参考链接


📝 摘要

智能指针(Smart Pointers)是 Rust 中实现高级内存管理模式的关键工具。它们不仅提供了指针的功能,还附带了额外的元数据和能力。本文将深入讲解 Box<T>Rc<T>Arc<T>RefCell<T> 等核心智能指针的使用场景、内部机制以及性能考量,帮助读者在复杂场景下做出正确的设计选择。


一、智能指针概述

1.1 什么是智能指针?

智能指针 vs 普通引用

在这里插入图片描述

特性对比表

类型 所有权 线程安全 运行时开销 使用场景
&T 借用 默认选择
Box<T> 独占 堆分配 递归类型
Rc<T> 共享 引用计数 单线程共享
Arc<T> 共享 原子计数 多线程共享
RefCell<T> 独占 运行时检查 内部可变性

二、Box:堆上的数据

2.1 基础用法

fn main() {
    // 在堆上分配单个值
    let b = Box::new(5);
    println!("b = {}", b);
    
    // Box 拥有数据的所有权
    let x = Box::new(String::from("hello"));
    // let y = x;  // 移动所有权
    // println!("{}", x);  // ❌ 编译错误
    
    // 自动解引用
    let boxed = Box::new(10);
    let value = *boxed + 5;
    println!("value = {}", value);
}

2.2 递归类型

// ❌ 编译错误:无限大小
// enum List {
//     Cons(i32, List),
//     Nil,
// }

// ✓ 使用 Box 解决
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)
            ))
        ))
    );
    
    print_list(&list);
}

fn print_list(list: &List) {
    match list {
        Cons(value, next) => {
            print!("{} -> ", value);
            print_list(next);
        },
        Nil => println!("Nil"),
    }
}

内存布局

栈                       堆
┌─────────────┐
│ list        │         ┌─────────────┐
│ Cons(1, ptr)├────────>│ Cons(2, ptr)├───┐
└─────────────┘         └─────────────┘   │
                                           ↓
                        ┌─────────────┐
                        │ Cons(3, ptr)├───┐
                        └─────────────┘   │
                                           ↓
                        ┌─────────────┐
                        │ Nil         │
                        └─────────────┘

2.3 trait 对象

trait Animal {
    fn make_sound(&self) -> &str;
}

struct Dog;
struct Cat;

impl Animal for Dog {
    fn make_sound(&self) -> &str {
        "Woof!"
    }
}

impl Animal for Cat {
    fn make_sound(&self) -> &str {
        "Meow!"
    }
}

fn main() {
    // Box 用于 trait 对象
    let animals: Vec<Box<dyn Animal>> = vec![
        Box::new(Dog),
        Box::new(Cat),
        Box::new(Dog),
    ];
    
    for animal in animals.iter() {
        println!("{}", animal.make_sound());
    }
}

三、Rc:单线程引用计数

3.1 共享所有权

use std::rc::Rc;

fn main() {
    let a = Rc::new(String::from("hello"));
    println!("引用计数: {}", Rc::strong_count(&a));  // 1
    
    let b = Rc::clone(&a);
    println!("引用计数: {}", Rc::strong_count(&a));  // 2
    
    {
        let c = Rc::clone(&a);
        println!("引用计数: {}", Rc::strong_count(&a));  // 3
    }
    
    println!("引用计数: {}", Rc::strong_count(&a));  // 2
}

3.2 共享数据结构

use std::rc::Rc;

enum List {
    Cons(i32, Rc<List>),
    Nil,
}

use List::{Cons, Nil};

fn main() {
    let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil)))));
    
    let b = Cons(3, Rc::clone(&a));
    let c = Cons(4, Rc::clone(&a));
    
    println!("a 的引用计数: {}", Rc::strong_count(&a));  // 3
}

内存结构

  b: Cons(3, ─┐
              ↓
  a: Cons(5, Rc) → Cons(10, Rc) → Nil
              ↑
  c: Cons(4, ─┘

引用计数: 3

3.3 弱引用(Weak)

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

#[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![]),
    });
    
    println!("leaf 强引用计数: {}", Rc::strong_count(&leaf));  // 1
    println!("leaf 弱引用计数: {}", Rc::weak_count(&leaf));    // 0
    
    {
        let branch = Rc::new(Node {
            value: 5,
            parent: RefCell::new(Weak::new()),
            children: RefCell::new(vec![Rc::clone(&leaf)]),
        });
        
        *leaf.parent.borrow_mut() = Rc::downgrade(&branch);
        
        println!("branch 强引用计数: {}", Rc::strong_count(&branch));  // 1
        println!("branch 弱引用计数: {}", Rc::weak_count(&branch));    // 1
        
        println!("leaf 强引用计数: {}", Rc::strong_count(&leaf));  // 2
    }
    
    println!("leaf 父节点: {:?}", leaf.parent.borrow().upgrade());
}

四、Arc:线程安全的引用计数

4.1 多线程共享数据

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..5 {
        let data_clone = Arc::clone(&data);
        let handle = thread::spawn(move || {
            let sum: i32 = data_clone.iter().sum();
            println!("线程 {} 计算和: {}", i, sum);
        });
        handles.push(handle);
    }
    
    for handle in handles {
        handle.join().unwrap();
    }
    
    println!("最终引用计数: {}", Arc::strong_count(&data));
}

4.2 Arc + Mutex 组合

use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];
    
    for _ in 0..10 {
        let counter = Arc::clone(&counter);
        let handle = thread::spawn(move || {
            let mut num = counter.lock().unwrap();
            *num += 1;
        });
        handles.push(handle);
    }
    
    for handle in handles {
        handle.join().unwrap();
    }
    
    println!("最终结果: {}", *counter.lock().unwrap());
}

4.3 性能考量

use std::sync::Arc;
use std::thread;
use std::time::Instant;

fn benchmark_arc() {
    let data = Arc::new(vec![0; 1_000_000]);
    let start = Instant::now();
    
    let handles: Vec<_> = (0..4)
        .map(|_| {
            let data = Arc::clone(&data);
            thread::spawn(move || {
                let _sum: i32 = data.iter().sum();
            })
        })
        .collect();
    
    for handle in handles {
        handle.join().unwrap();
    }
    
    println!("Arc 用时: {:?}", start.elapsed());
}

fn main() {
    benchmark_arc();
}

五、RefCell:内部可变性

5.1 运行时借用检查

use std::cell::RefCell;

fn main() {
    let data = RefCell::new(5);
    
    // 不可变借用
    let r1 = data.borrow();
    let r2 = data.borrow();
    println!("r1 = {}, r2 = {}", r1, r2);
    drop(r1);
    drop(r2);
    
    // 可变借用
    let mut r3 = data.borrow_mut();
    *r3 += 1;
    println!("修改后: {}", *r3);
    drop(r3);
    
    // ❌ 运行时panic
    // let r4 = data.borrow();
    // let r5 = data.borrow_mut();  // panic: 已存在不可变借用
}

5.2 Rc + RefCell 组合

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

#[derive(Debug)]
struct Node {
    value: i32,
    next: Option<Rc<RefCell<Node>>>,
}

fn main() {
    let first = Rc::new(RefCell::new(Node {
        value: 1,
        next: None,
    }));
    
    let second = Rc::new(RefCell::new(Node {
        value: 2,
        next: None,
    }));
    
    // 修改 first 的 next 指向 second
    first.borrow_mut().next = Some(Rc::clone(&second));
    
    // 修改 second 的值
    second.borrow_mut().value = 20;
    
    println!("first: {:?}", first);
}

5.3 实战:可变计数器

use std::cell::RefCell;

struct Counter {
    count: RefCell<i32>,
}

impl Counter {
    fn new() -> Self {
        Counter {
            count: RefCell::new(0),
        }
    }
    
    fn increment(&self) {
        *self.count.borrow_mut() += 1;
    }
    
    fn get(&self) -> i32 {
        *self.count.borrow()
    }
}

fn main() {
    let counter = Counter::new();
    
    counter.increment();
    counter.increment();
    counter.increment();
    
    println!("计数: {}", counter.get());
}

六、Cell:简单内部可变性

use std::cell::Cell;

struct Config {
    debug: Cell<bool>,
}

impl Config {
    fn new() -> Self {
        Config {
            debug: Cell::new(false),
        }
    }
    
    fn set_debug(&self, value: bool) {
        self.debug.set(value);
    }
    
    fn is_debug(&self) -> bool {
        self.debug.get()
    }
}

fn main() {
    let config = Config::new();
    println!("调试模式: {}", config.is_debug());
    
    config.set_debug(true);
    println!("调试模式: {}", config.is_debug());
}

七、性能对比与选择指南

7.1 性能基准测试

use std::rc::Rc;
use std::sync::Arc;
use std::time::Instant;

fn benchmark() {
    const N: usize = 1_000_000;
    
    // Box 性能
    let start = Instant::now();
    for _ in 0..N {
        let _b = Box::new(42);
    }
    println!("Box: {:?}", start.elapsed());
    
    // Rc 性能
    let start = Instant::now();
    let rc = Rc::new(42);
    for _ in 0..N {
        let _clone = Rc::clone(&rc);
    }
    println!("Rc clone: {:?}", start.elapsed());
    
    // Arc 性能
    let start = Instant::now();
    let arc = Arc::new(42);
    for _ in 0..N {
        let _clone = Arc::clone(&arc);
    }
    println!("Arc clone: {:?}", start.elapsed());
}

fn main() {
    benchmark();
}

7.2 选择决策树

在这里插入图片描述

选择指南表

场景 推荐方案 原因
递归数据结构 Box<T> 固定大小
单线程共享 Rc<T> 低开销
多线程共享 Arc<T> 线程安全
共享+修改(单线程) Rc<RefCell<T>> 内部可变
共享+修改(多线程) Arc<Mutex<T>> 线程安全

八、实战案例

8.1 案例1:图数据结构

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

#[derive(Debug)]
struct Node {
    value: i32,
    edges: RefCell<Vec<Rc<Node>>>,
    parent: RefCell<Weak<Node>>,
}

impl Node {
    fn new(value: i32) -> Rc<Self> {
        Rc::new(Node {
            value,
            edges: RefCell::new(vec![]),
            parent: RefCell::new(Weak::new()),
        })
    }
    
    fn add_edge(&self, node: &Rc<Node>) {
        self.edges.borrow_mut().push(Rc::clone(node));
        *node.parent.borrow_mut() = Rc::downgrade(&Rc::new(Node {
            value: self.value,
            edges: RefCell::new(vec![]),
            parent: RefCell::new(Weak::new()),
        }));
    }
}

fn main() {
    let node1 = Node::new(1);
    let node2 = Node::new(2);
    let node3 = Node::new(3);
    
    node1.add_edge(&node2);
    node1.add_edge(&node3);
    
    println!("Node1 edges: {}", node1.edges.borrow().len());
}

8.2 案例2:线程池

use std::sync::{Arc, Mutex};
use std::thread;
use std::sync::mpsc;

type Job = Box<dyn FnOnce() + Send + 'static>;

struct ThreadPool {
    workers: Vec<Worker>,
    sender: mpsc::Sender<Job>,
}

impl ThreadPool {
    fn new(size: usize) -> Self {
        let (sender, receiver) = mpsc::channel();
        let receiver = Arc::new(Mutex::new(receiver));
        
        let mut workers = Vec::with_capacity(size);
        
        for id in 0..size {
            workers.push(Worker::new(id, Arc::clone(&receiver)));
        }
        
        ThreadPool { workers, sender }
    }
    
    fn execute<F>(&self, f: F)
    where
        F: FnOnce() + Send + 'static,
    {
        let job = Box::new(f);
        self.sender.send(job).unwrap();
    }
}

struct Worker {
    id: usize,
    thread: thread::JoinHandle<()>,
}

impl Worker {
    fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Job>>>) -> Self {
        let thread = thread::spawn(move || loop {
            let job = receiver.lock().unwrap().recv();
            
            match job {
                Ok(job) => {
                    println!("Worker {} 执行任务", id);
                    job();
                },
                Err(_) => break,
            }
        });
        
        Worker { id, thread }
    }
}

fn main() {
    let pool = ThreadPool::new(4);
    
    for i in 0..8 {
        pool.execute(move || {
            println!("任务 {} 完成", i);
            thread::sleep(std::time::Duration::from_secs(1));
        });
    }
    
    thread::sleep(std::time::Duration::from_secs(3));
}

九、总结与讨论

智能指针是 Rust 实现高级内存管理的核心工具:

✅ 灵活性:支持多种所有权模式
✅ 安全性:编译期+运行时双重保障
✅ 高性能:针对场景优化
✅ 可组合:Rc+RefCell, Arc+Mutex

核心概念总结

在这里插入图片描述

讨论问题

  1. 何时应该使用 Rc<RefCell<T>> 而非重新设计数据结构?
  2. Arc 的性能开销在什么场景下可接受?
  3. 如何避免 Rc 造成的循环引用?

欢迎分享经验!💬


参考链接

  1. Rust Book - Smart Pointers:https://doc.rust-lang.org/book/ch15-00-smart-pointers.html
  2. std::rc 文档:https://doc.rust-lang.org/std/rc/
  3. std::sync::Arc 文档:https://doc.rust-lang.org/std/sync/struct.Arc.html
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐