引用计数有一个经典缺陷:循环引用会让计数永远不归零。Rust 没有垃圾回收器,所以这是真泄漏。这篇用带 Drop 的节点把泄漏「看见」,再用 Weak 把循环打断,并验证计数与 upgrade 的行为。
源码:
1 | use std::cell::RefCell;
2 | use std::rc::Rc;
3 | struct Node { name: &'static str, next: RefCell<Option<Rc<Node>>> }
4 | impl Drop for Node { fn drop(&mut self) { println!(" drop {}", self.name); } }
5 | fn main() {
6 | println!("-- 没有循环:离开作用域时两个节点都被 drop");
7 | { let a = Rc::new(Node { name: "a1", next: RefCell::new(None) }); let b = Rc::new(Node { name: "b1", next: RefCell::new(Some(Rc::clone(&a))) }); let _ = &b; }
8 | println!("-- 有循环:a2 → b2 → a2");
9 | {
10 | let a = Rc::new(Node { name: "a2", next: RefCell::new(None) });
11 | let b = Rc::new(Node { name: "b2", next: RefCell::new(Some(Rc::clone(&a))) });
12 | *a.next.borrow_mut() = Some(Rc::clone(&b));
13 | println!(" a 的强引用数 = {},b 的强引用数 = {}", Rc::strong_count(&a), Rc::strong_count(&b));
14 | }
15 | println!("-- 块结束了,但没有看到 drop a2 / drop b2:它们泄漏了");
16 | }
编译:成功
运行(退出码 0):
-- 没有循环:离开作用域时两个节点都被 drop
drop b1
drop a1
-- 有循环:a2 → b2 → a2
a 的强引用数 = 2,b 的强引用数 = 2
-- 块结束了,但没有看到 drop a2 / drop b2:它们泄漏了 源码:
1 | use std::rc::{Rc, Weak};
2 | struct D(&'static str);
3 | impl Drop for D { fn drop(&mut self) { println!(" drop {}", self.0); } }
4 | fn main() {
5 | let strong = Rc::new(D("数据"));
6 | let weak: Weak<D> = Rc::downgrade(&strong);
7 | println!("strong_count = {},weak_count = {}", Rc::strong_count(&strong), Rc::weak_count(&strong));
8 | println!("upgrade() 时数据还在:{}", weak.upgrade().map(|d| d.0).unwrap_or("已释放"));
9 | println!("upgrade 之后(临时 Rc 已释放)strong_count = {}", Rc::strong_count(&strong));
10 | drop(strong);
11 | println!("drop(strong) 之后,upgrade() = {:?}", weak.upgrade().map(|d| d.0));
12 | }
编译:成功
运行(退出码 0):
strong_count = 1,weak_count = 1
upgrade() 时数据还在:数据
upgrade 之后(临时 Rc 已释放)strong_count = 1
drop 数据
drop(strong) 之后,upgrade() = None 源码:
1 | use std::cell::RefCell;
2 | use std::rc::{Rc, Weak};
3 | struct Node { name: &'static str, parent: RefCell<Weak<Node>>, children: RefCell<Vec<Rc<Node>>> }
4 | impl Drop for Node { fn drop(&mut self) { println!(" drop {}", self.name); } }
5 | fn main() {
6 | let leaf = Rc::new(Node { name: "leaf", parent: RefCell::new(Weak::new()), children: RefCell::new(vec![]) });
7 | println!("leaf 的父节点(挂接前) = {:?}", leaf.parent.borrow().upgrade().map(|p| p.name));
8 | {
9 | let branch = Rc::new(Node { name: "branch", parent: RefCell::new(Weak::new()), children: RefCell::new(vec![Rc::clone(&leaf)]) });
10 | *leaf.parent.borrow_mut() = Rc::downgrade(&branch);
11 | println!("leaf 的父节点(挂接后) = {:?}", leaf.parent.borrow().upgrade().map(|p| p.name));
12 | println!("branch:strong = {},weak = {};leaf:strong = {},weak = {}", Rc::strong_count(&branch), Rc::weak_count(&branch), Rc::strong_count(&leaf), Rc::weak_count(&leaf));
13 | }
14 | println!("branch 离开作用域之后,leaf 的父节点 = {:?}(leaf 的 strong_count = {})", leaf.parent.borrow().upgrade().map(|p| p.name), Rc::strong_count(&leaf));
15 | }
编译:成功
运行(退出码 0):
leaf 的父节点(挂接前) = None
leaf 的父节点(挂接后) = Some("branch")
branch:strong = 1,weak = 1;leaf:strong = 2,weak = 0
drop branch
branch 离开作用域之后,leaf 的父节点 = None(leaf 的 strong_count = 1)
drop leafRefCell 的作用见RefCell<T> 运行时借用检查;Rc 的基本行为见Rc<T>。