capypad
0 day streak
rust / intermediate
Snippet

Smart Pointer Cycles with Rc and RefCell

Rc (Reference Counting) allows multiple ownership, but cycles cause memory leaks. Using Weak references breaks cycles - a Weak does not increment the strong count. The upgrade() method converts Weak back to Option<Rc>. In this tree structure, parent holds a Weak reference to avoid retaining children, while children hold strong Rc references to their parent. Rc::downgrade creates a Weak from an Rc, and the borrow checker ensures no reference cycles exist at compile time for non-cell types.

snippet.rs
rust
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
use std::cell::RefCell;
use std::rc::{Rc, Weak};
 
#[derive(Debug)]
struct Node {
value: i32,
children: RefCell<Vec<Rc<Node>>>,
parent: RefCell<Weak<Node>>,
}
 
impl Node {
fn new(value: i32) -> Rc<Self> {
Rc::new(Node {
value,
children: RefCell::new(Vec::new()),
parent: RefCell::new(Weak::new()),
})
}
 
fn add_child(&self, child: Rc<Node>) {
self.children.borrow_mut().push(Rc::clone(&child));
*child.parent.borrow_mut() = Rc::downgrade(self);
}
 
fn get_parent(&self) -> Option<Rc<Node>> {
self.parent.borrow().upgrade()
}
}
 
fn main() {
let leaf = Node::new(3);
let branch = Node::new(5);
branch.add_child(Rc::clone(&leaf));
 
println!("Leaf parent: {:?}", leaf.get_parent());
println!("Branch strong count: {}", Rc::strong_count(&branch));
println!("Leaf strong count: {}", Rc::strong_count(&leaf));
}
Breakdown
1
parent: RefCell<Weak<Node>>
Weak does not prevent Node from being dropped; prevents strong cycles
2
Rc::downgrade(self)
Creates a Weak pointer to the Rc; does not increment reference count
3
child.parent.borrow_mut() = Rc::downgrade(self);
Sets child's parent weak reference back to this node
4
self.parent.borrow().upgrade()
Converts Weak to Option<Rc>; None if parent was dropped