Rust - Rc<T>

162 阅读2分钟

在 Rust 里,Rc<T>(引用计数智能指针)是实现共享所有权的工具。要是你需要让多个变量拥有同一数据的所有权,就可以借助 Rc<T> 来达成。

核心概念

  1. 共享所有权机制 Rc<T> 借助引用计数的方式,让同一数据能被多个所有者共同持有。每回克隆 Rc<T> 时,引用计数就会增加;当克隆体超出作用域被丢弃时,引用计数则会减少。一旦引用计数变为 0,数据就会被自动清理。

  2. 不可变借用限制 Rc<T> 仅支持不可变借用。要是你尝试通过 Rc<T> 修改其内部数据,就会在编译阶段报错。这一限制是为了保证 Rust 的内存安全,避免出现数据竞争的情况。

  3. 使用场景

Rc<T> 适用于单线程环境下的数据共享。比如在图结构中,多个节点可能会引用同一个子节点;或者在需要共享只读数据的场景中,都可以使用 Rc<T>。不过要注意,它不能在多线程环境中使用。

代码示例

下面通过代码来展示 Rc<T> 的基本用法:

use std::rc::Rc; 
fn main() { 
    // 创建一个 Rc<T>,其中 T 是 String 
    let shared_data = Rc::new("Hello, Rc<T>!".to_string()); 
    // 克隆 Rc<T>,增加引用计数 
    let clone1 = Rc::clone(&shared_data); 
    let clone2 = shared_data.clone(); // 等价于 Rc::clone(&shared_data) 
    // 打印引用计数(调试时使用) 
    println!("引用计数: {}", Rc::strong_count(&shared_data)); // 输出 3 
    // 通过克隆体访问数据(只能不可变借用) 
    println!("clone1: {}", clone1); 
    println!("clone2: {}", clone2);
    // 当 clone1 和 clone2 超出作用域时,引用计数减 1 
    // 当 shared_data 超出作用域时,引用计数变为 0,数据被释放 
} 

引用计数的工作原理

  • 创建时:当你使用 Rc::new(data) 创建 Rc<T> 时,引用计数初始化为 1。
  • 克隆时:调用 Rc::clone(&rc) 会使引用计数加 1。这里的克隆操作开销不大,只是复制了指针并更新了引用计数。
  • 释放时:当某个克隆体超出作用域时,引用计数减 1。当引用计数变为 0 时,数据会被销毁。

与其他指针的对比

  • Box<T> 提供的是单一所有权,同一数据只能有一个所有者。
  • Rc<T> 允许多个所有者共享同一数据,但只支持不可变借用。
  • Arc<T> 是线程安全的引用计数指针,适用于多线程环境。

注意事项

  • 循环引用问题:如果使用 Rc<T> 形成了循环引用,比如 A 引用 B,B 又引用 A,那么引用计数永远不会变为 0,这会导致内存泄漏。解决循环引用问题可以使用 Weak<T>
  • 性能开销:引用计数的更新操作会带来一定的性能开销,所以 Rc<T> 适合用于读多写少的场景。