Rc 共享所有权
你会学到什么
Rc<T>通过引用计数允许多个所有者共享同一份数据。Rc::clone只增加计数,不复制底层数据,代价很小。Rc只用于单线程;跨线程要用Arc(并发阶段介绍)。
最小示例
use std::rc::Rc;
let shared = Rc::new(String::from("data"));
let a = Rc::clone(&shared);
println!("count = {}", Rc::strong_count(&shared)); // 2
运行代码
cd examples
cargo run -p rt_23_rc
cargo test -p rt_23_rc
代码讲解
Rc::clone 是浅拷贝——只是把引用计数加一,所有克隆指向同一块堆数据:
let a = Rc::clone(&shared);
let b = Rc::clone(&shared);
// strong_count == 3
当最后一个 Rc 被 drop、计数归零时,数据才被释放。Rc::ptr_eq 可以判断两个 Rc 是否指向同一块内存。
习惯上写
Rc::clone(&x)而不是x.clone(),以强调这是“增加引用计数”而非深拷贝。
常见错误
想通过 Rc 修改共享数据:
let shared = Rc::new(vec![1, 2, 3]);
shared.push(4); // ❌ Rc<T> 默认不可变
Rc<T> 只给共享只读访问。要共享 + 可变,需要 Rc<RefCell<T>>(下一章)。
练习
- 构造一个树,让两个父节点共享同一个子节点。
- 打印每次
clone和drop后的strong_count,观察变化。
小结
Rc<T> 用引用计数实现共享所有权,适合“一份数据、多处只读引用”的场景,仅限单线程。
下一步
Rc 共享只读,要在共享时还能修改,就需要内部可变性。下一章学习 RefCell。
完整示例代码
下面是 examples/23_rc/ 的完整源码。无需 clone 仓库,直接在页面上阅读、复制、对照运行。
examples/23_rc/src/main.rs
//! Rc<T>:单线程下的共享所有权(引用计数)。
use std::rc::Rc;
/// 一个被多处共享的配置。
#[derive(Debug, PartialEq)]
struct Config {
name: String,
}
fn main() {
let shared = Rc::new(Config {
name: "prod".to_string(),
});
println!("count after create = {}", Rc::strong_count(&shared));
// clone 只增加引用计数,不复制底层数据。
let a = Rc::clone(&shared);
let b = Rc::clone(&shared);
println!("count after 2 clones = {}", Rc::strong_count(&shared));
println!("a.name = {}, b.name = {}", a.name, b.name);
drop(a);
drop(b);
println!("count after drops = {}", Rc::strong_count(&shared));
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn clone_increases_count() {
let shared = Rc::new(Config {
name: "x".to_string(),
});
assert_eq!(Rc::strong_count(&shared), 1);
let clone = Rc::clone(&shared);
assert_eq!(Rc::strong_count(&shared), 2);
drop(clone);
assert_eq!(Rc::strong_count(&shared), 1);
}
#[test]
fn clones_share_data() {
let shared = Rc::new(Config {
name: "same".to_string(),
});
let clone = Rc::clone(&shared);
// 指向同一块数据。
assert!(Rc::ptr_eq(&shared, &clone));
}
} examples/23_rc/Cargo.toml
[package]
name = "rt_23_rc"
version.workspace = true
edition.workspace = true
publish.workspace = true