Course Builder个人学习工作台 · 本地模式
Rust:从所有权到可测试的命令行工具

16|智能指针:区分独占与共享所有权

目标:根据拥有者数量区分 Box<T> 与 Rc<T>,验证克隆共享句柄不等于复制内部数据。

TextLab 的单文件路径只需普通拥有值和借用,暂时不需要智能指针。不过,当多个长期存在的组件都需要保留同一份报告时,所有权关系会发生变化。此时先问“谁需要拥有”,再选择类型。

Box<T> 在堆上保存一个 T,盒子通常是独占拥有者,移动盒子会转移拥有权。它适合需要间接存储或已知大小指针的场景,例如递归类型;把本来很小的报告装进 Box 只是教学实验,不代表一定更快。

Rc<T> 为同一份数据提供多个单线程拥有句柄,通过引用计数管理释放。Rc::clone 增加强引用数,不克隆内部的 T。最后一个强引用消失时,内部值被丢弃;普通共享访问不能据此随意修改内部值。共享拥有与共享可变访问是两个不同问题。

观察计数变化

从第 15 课项目继续,创建 examples/pointers.rs:

use std::rc::Rc;
use textlab::analyze;

fn main() {
    let boxed = Box::new(analyze("rust"));
    println!("boxed words={}", boxed.words);

    let report = Rc::new(analyze("你好 Rust\n"));
    println!("owners={}", Rc::strong_count(&report));
    {
        let second = Rc::clone(&report);
        assert!(Rc::ptr_eq(&report, &second));
        println!("owners={} chars={}", Rc::strong_count(&report), second.chars);
    }
    println!("owners={}", Rc::strong_count(&report));
}

运行前先预测:内层大括号结束,会使报告立即失效吗?

cargo run --quiet --example pointers

预期输出:

boxed words=1
owners=1
owners=2 chars=8
owners=1

字段访问可经过智能指针的解引用能力访问内部报告。ptr_eq 验证两个 Rc 指向同一份分配,而不只是“内容恰好相等”。内层 second 被丢弃后,外层 report 仍是一个拥有者,所以数据继续存在。

增加第三个拥有者

  1. 在内层 second 之后增加 let third = Rc::clone(&report);。
  2. 在内层输出前加入 assert!(Rc::ptr_eq(&second, &third));。
  3. 再运行,内层计数应从 2 变成 3;离开大括号后仍为 1。
  4. 恢复主示例。这里计数变化来自句柄数量,没有复制报告字段。

独立练习:让共享字符串的原始句柄在内层作用域结束时销毁,但保留一个克隆句柄在外层使用;断言此时强引用数为 1。

完整参考实现

可完整替换本课 example:

use std::rc::Rc;

fn main() {
    let saved;
    {
        let original = Rc::new(String::from("shared"));
        saved = Rc::clone(&original);
        assert_eq!(Rc::strong_count(&original), 2);
    }
    assert_eq!(Rc::strong_count(&saved), 1);
    println!("{saved}");
}

运行同一命令,预期 shared。如果改成 saved = &original,留下的只是借用,无法越过 original 的作用域;拥有一个 Rc 句柄与借用一个 Rc 句柄不是同一件事。

如果试图把 Rc 交给新线程,编译器会拒绝,因为它的计数机制不适合跨线程共享;跨线程共享拥有通常使用 Arc,可变共享状态还需要合适的同步机制。如果想通过两个 Rc 直接修改内部 String,先重审是否真的需要多个可变消费者,不要把共享拥有误解成取消借用规则。Rc 形成强引用环时可能泄漏,需要 Weak 等设计;本例没有环。

检查点:新增 examples/pointers.rs,主程序与库保持不变。这个实验扩展你的类型选择能力,不把不必要的共享结构加入 TextLab。

参考资料