智能指针(1):Box、Rc 与独占修改
从独占所有权出发,观察堆上间接存储、引用计数与可变访问之间的关系。
学习日期:2026-10-11。本篇与同日的「智能指针」其他篇章合计为一个模块、一天学习量。
文中的完整代码保留当前示例的有效语句,省去原文件中的注释与已注释实验行;实验变体另行说明。运行命令在项目根目录执行,示例名称由 Cargo.toml 注册。
Box:用间接存储表达递归结构
Node 中的 next 表示下一个节点:None 是链尾,Some(Box<Node>) 指向堆上的后继节点。若直接写成 Option<Node>,类型会无限递归,编译器无法确定大小;Box 提供大小确定的间接层。
这里第一个 Node 是局部值,第二个节点由 Box 管理。Box 不等于共享所有权:将它赋给另一个变量通常会移动所有权,原变量不能继续使用。离开作用域时,节点和其拥有的后继节点会被依次释放。
struct Node { value: i32, next: Option<Box<Node>>,}
fn main() { let first = Node { value: 10, next: Some(Box::new(Node { value: 20, next: None, })), };
println!("第一个节点: {}", first.value);
if let Some(second) = &first.next { println!("第二个节点: {}", second.value); }}运行:
cargo run --example box_basic本次本地运行的标准输出如下。并发日志只代表这次运行,先后关系以正文说明为准。
第一个节点: 10第二个节点: 20理解与实验
if let Some(second) = &first.next 借用后继节点,没有把 Box 从 first 中移走。这里借用足以读取,没必要克隆或转移节点。
小练习:将节点扩展为 10 → 20 → 30,沿着 next 借用读取,观察链尾如何表示。
Rc:多个所有者共享同一份数据
Rc<T> 适合单线程共享所有权。Rc::clone(&first) 增加强引用计数,两个句柄指向同一个 String,不会深拷贝字符串内容。
本例依次展示计数 1 → 2 → 1。drop(first) 消耗的是一个句柄;second 仍然持有强引用,所以数据还在。最后一个强引用消失时,内部值才会释放。
use std::rc::Rc;
fn main() { let first = Rc::new(String::from("hello"));
println!("创建后: {}", Rc::strong_count(&first));
let second = Rc::clone(&first);
println!("克隆后: {}", Rc::strong_count(&first)); println!("first: {}", first); println!("second: {}", second);
drop(first);
println!("释放 first 后: {}", Rc::strong_count(&second));}运行:
cargo run --example rc_basic本次本地运行的标准输出如下。并发日志只代表这次运行,先后关系以正文说明为准。
创建后: 1克隆后: 2first: hellosecond: hello释放 first 后: 1理解与实验
不要把 Rc::clone 与内部值的 clone 混淆:前者复制共享句柄,后者可能复制数据。Rc 也不允许通过多个共享句柄直接获得 &mut T。
小练习:使用 Rc::ptr_eq(&first, &second) 检查两个句柄是否指向同一份分配;这行应放在 drop(first) 之前。
Rc::get_mut:独占时直接修改
let mut text 只允许可变借用或重新赋值这个句柄,不会让共享数据自动可变。Rc::get_mut(&mut text) 返回 Option<&mut String>,由它判断当前是否能独占内部值。
第一次没有其他引用,得到 Some,追加 rust;创建 other 后得到 None;释放 other 后又能追加 !。
use std::rc::Rc;
fn main() { let mut text = Rc::new(String::from("hello"));
if let Some(value) = Rc::get_mut(&mut text) { value.push_str(" rust"); }
println!("第一次修改:{}", text);
let other = Rc::clone(&text);
println!("共享时可修改:{}", Rc::get_mut(&mut text).is_some());
drop(other);
if let Some(value) = Rc::get_mut(&mut text) { value.push('!'); }
println!("第二次修改:{}", text);}运行:
cargo run --example rc_get_mut本次本地运行的标准输出如下。并发日志只代表这次运行,先后关系以正文说明为准。
第一次修改:hello rust共享时可修改:false第二次修改:hello rust!理解与实验
独占条件还包括弱引用:只要有其他 Rc 或 Weak 指向同一分配,get_mut 就会返回 None。可以在 drop(other) 后创建 let observer = Rc::downgrade(&text); 验证,此时最后的 ! 不会追加。
这与 RefCell 的定位不同:get_mut 依赖当前确实没有其他引用,不能作为一般的共享可变方案。
同模块笔记
标准库参考
Rust 学习笔记
连载中分享文章
生成精美分享图或复制链接,与更多人分享本文。
继续阅读
换条路线
从其他文章中稳定抽取
最后更新于 ,距今已过 4 天
部分内容可能已过时