Rust智能指针Rc<T> 使用总结

Rust 程序设计语言第 15.4 节《Rc<T> 引用计数智能指针》的核心内容总结如下:

1. 为什么需要 Rc<T>?

  • 多所有权场景:在 Rust 严格的所有权规则下,一个值通常只有一个所有者。但在某些数据结构(如图、树中多个分支共享子节点)中,单个值可能需要有多个所有者。
  • 编译时未知生命周期 :当需要在堆上分配数据供程序的多个部分读取,且无法在编译时确定哪一部分会最后结束使用该数据 时,普通的 Box<T> 或引用无法满足需求(会导致所有权被移动或生命周期标注过于复杂)。
  • 解决方案 :Rc<T> 是 Reference Counted(引用计数) 的缩写。它通过在底层维护一个指向堆上数据的引用计数,来跟踪当前有多少个所有者正在使用该数据。只要引用计数大于 0,数据就一直有效;当引用计数归零时,数据会被自动清理。

2. 使用限制

  • 仅限单线程 :Rc<T> 只能用于单线程场景 。如果用于多线程,会导致引用计数不安全(它没有实现线程安全的 Send 和 Sync)。多线程环境下的引用计数需要使用 Arc<T>。
  • 只读共享 :Rc<T> 提供的只能是不可变引用 (只读数据)。它不允许通过多个可变引用修改数据,以防止违反 Rust 的借用规则(避免数据竞争)。若需在共享的同时修改数据,通常需要配合 RefCell<T>(内部可变性模式)。

3. 代码示例对比

  • Box<T> 的局限 :
    若尝试让两个链表 b 和 c 共享同一个子列表 a(使用 Box<T>),当 b 包含 a 时,a 的所有权会被移动(move),导致无法再将其赋给 c,从而引发编译错误。
  • 使用 Rc<T> 解决 :
    将 List 定义中的 Box<List> 替换为 Rc<List>。创建 b 和 c 时,通过克隆 Rc 指针来共享所有权:
rust 复制代码
use std::rc::Rc;

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

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

    // 使用 Rc::clone(&a) 增加引用计数,而不是移动所有权
    let b = List::Cons(3, Rc::clone(&a));
    let c = List::Cons(4, Rc::clone(&a));
}

4. 关键特性与惯用法

  • **Rc::clone(&a) vs a.clone()**:

  • 两者效果相同,但 Rust 的惯例 是使用 Rc::clone(&a)。

  • Rc::clone 的实现不会对底层数据进行深拷贝(Deep Copy),它仅仅是将引用计数加 1,操作非常快。

  • 这种写法能在视觉上与"深拷贝式的 clone"区分开,方便在排查性能问题时迅速定位到耗时的深拷贝操作。

  • 检查引用计数 (Rc::strong_count):

  • 可以通过 Rc::strong_count(&a) 查看当前的强引用计数。

  • 当作用域结束或变量被丢弃时,Drop trait 会自动将引用计数减 1,无需手动调用减少计数的函数。

相关推荐
Kapaseker1 小时前
删掉 1.1 万行 C 代码,Rust 正式接手 Android Binder
android·rust
全栈陈序员1 小时前
【Python】基础语法入门(三十三)—— 日志系统详解:用 logging 记录程序运行
java·开发语言·python
ESDWAN1 小时前
SD-WAN 是什么?工作原理、适用场景与选型要点
开发语言·网络·数据库·架构·php
万少1 小时前
Harmony 7.0 这个skill帮你自动化操作AGC
前端·javascript·后端
huaweichenai1 小时前
spring boot对接kafka实现消息队列处理
spring boot·后端·kafka
treasure1401 小时前
Django复习笔记(2026年10月)
后端·python·django·api·web·drf
iCxhust1 小时前
C#双时区时钟设计
开发语言·c#
sbjdhjd1 小时前
从污点追踪断点到 PHP-FPM 请求队列:HIDS PHP WebShell 样本复盘
开发语言·网络安全·数据挖掘·渗透测试·开源·php·shell