Rc 解决的是「单线程里一份数据有多个所有者」。这篇用真实输出把它的核心行为逐条验证:计数怎么变、数据为什么只读以及怎么修改、为什么不能跨线程,以及它在内存里的样子和 clone 的成本。
源码:
1 | use std::rc::Rc;
2 | fn main() {
3 | let a = Rc::new(String::from("共享数据"));
4 | println!("创建 a 之后:strong_count = {}", Rc::strong_count(&a));
5 | let b = Rc::clone(&a);
6 | println!("clone 出 b 之后:strong_count = {}", Rc::strong_count(&a));
7 | {
8 | let c = Rc::clone(&a);
9 | println!("内层再 clone 出 c:strong_count = {}(a、b、c 指向同一块内存:{})", Rc::strong_count(&a), Rc::ptr_eq(&a, &c));
10 | }
11 | println!("c 离开作用域之后:strong_count = {}", Rc::strong_count(&a));
12 | drop(b);
13 | println!("drop(b) 之后:strong_count = {}", Rc::strong_count(&a));
14 | }
编译:成功
运行(退出码 0):
创建 a 之后:strong_count = 1
clone 出 b 之后:strong_count = 2
内层再 clone 出 c:strong_count = 3(a、b、c 指向同一块内存:true)
c 离开作用域之后:strong_count = 2
drop(b) 之后:strong_count = 1 源码:
1 | use std::rc::Rc;
2 | fn main() {
3 | let a = Rc::new(vec![1, 2, 3]);
4 | a.push(4);
5 | }
编译:失败
error[E0596]: cannot borrow data in an `Rc` as mutable
--> <实验目录>/src/u29b.rs:4:5
|
4 | a.push(4);
| ^ cannot borrow as mutable
|
= help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc<Vec<i32>>`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0596`.
源码:
1 | use std::rc::Rc;
2 | fn main() {
3 | let mut a = Rc::new(vec![1, 2, 3]);
4 | println!("独占时 get_mut 成功:{}", Rc::get_mut(&mut a).map(|v| { v.push(4); true }).unwrap_or(false));
5 | let b = Rc::clone(&a);
6 | println!("有第二个所有者时 get_mut 返回 None:{}", Rc::get_mut(&mut a).is_none());
7 | Rc::make_mut(&mut a).push(5); // 写时复制:发现不是唯一所有者,先克隆一份
8 | println!("make_mut 之后:a = {:?},b = {:?},a 与 b 还指向同一块内存吗?{}", a, b, Rc::ptr_eq(&a, &b));
9 | }
编译:成功
运行(退出码 0):
独占时 get_mut 成功:true
有第二个所有者时 get_mut 返回 None:true
make_mut 之后:a = [1, 2, 3, 4, 5],b = [1, 2, 3, 4],a 与 b 还指向同一块内存吗?false 源码:
1 | use std::rc::Rc;
2 | use std::thread;
3 | fn main() {
4 | let a = Rc::new(5);
5 | let h = thread::spawn(move || println!("{}", a));
6 | h.join().unwrap();
7 | }
编译:失败
error[E0277]: `Rc<i32>` cannot be sent between threads safely
--> <实验目录>/src/u29d.rs:5:27
|
5 | let h = thread::spawn(move || println!("{}", a));
| ------------- -------^^^^^^^^^^^^^^^^^^
| | |
| | `Rc<i32>` cannot be sent between threads safely
| | within this `{closure@u29d.rs:5:27}`
| required by a bound introduced by this call
|
= help: within `{closure@u29d.rs:5:27}`, the trait `Send` is not implemented for `Rc<i32>`
note: required because it's used within this closure
--> <实验目录>/src/u29d.rs:5:27
|
5 | let h = thread::spawn(move || println!("{}", a));
| ^^^^^^^
note: required by a bound in `spawn`
--> <rust 标准库源码>/library/std/src/thread/functions.rs:128:8
|
125 | pub fn spawn<F, T>(f: F) -> JoinHandle<T>
| ----- required by a bound in this function
...
128 | F: Send + 'static,
| ^^^^ required by this bound in `spawn`
= note: the full name for the type has been written to '<实验目录>/bin/u29d.long-type-13928888389995968006.txt'
= note: consider using `--verbose` to print the full type name to the console
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0277`.多线程要用 Arc,见Arc<T> 与跨线程共享所有权;Send 的含义见Send 与 Sync。
编译:成功
运行(退出码 0):
size_of::<Rc<u64>>() = 8 字节(指针);Rc::new(0u64) 在堆上分配了 24 字节(强引用计数 8 + 弱引用计数 8 + 数据 8)
Rc::clone(共享 1 MiB 数据):每次约 3.9 ns
Vec<u8>(1 MiB) 的 clone:每次约 19616 ns循环引用会让计数永远不归零,见Weak<T> 解决循环引用;需要修改共享数据时配合RefCell。内存布局见Box、Rc、Arc 的内存布局。