rust / intermediate
Snippet
Reference Cycles and Memory Leaks with Rc<T>
Rc<T> provides shared ownership via reference counting. Each clone increments the count, each drop decrements it. When count reaches zero, the value is deallocated. However,Rc cannot detect reference cycles leading to memory leaks. RefCell delays borrow checking to runtime, allowing mutable references through shared references.
snippet.rs
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
39
40
41
42
use std::rc::Rc;use std::cell::RefCell;struct Node {value: i32,children: RefCell<Vec<Rc<Node>>>,}impl Node {fn new(value: i32) -> Rc<Node> {Rc::new(Node {value,children: RefCell::new(vec![]),})}fn add_child(&self, child: Rc<Node>) {self.children.borrow_mut().push(child);}}fn main() {let root = Node::new(1);let leaf = Node::new(2);println!("Root strong count: {}", Rc::strong_count(&root));println!("Leaf strong count: {}", Rc::strong_count(&leaf));root.add_child(Rc::clone(&leaf));println!("After adding child:");println!("Root strong count: {}", Rc::strong_count(&root));println!("Leaf strong count: {}", Rc::strong_count(&leaf));let leaf_clone = Rc::clone(&leaf);drop(leaf);println!("After dropping original leaf:");println!("Leaf clone count: {}", Rc::strong_count(&leaf_clone));println!("Root has {} children", root.children.borrow().len());}
Breakdown
1
Rc::new(Node { ... })
Creates a new reference-counted Node on the heap
2
Rc::clone(&leaf)
Creates a new reference to the same allocation, increments count
3
Rc::strong_count(&root)
Returns current reference count, useful for debugging leaks
4
RefCell<Vec<Rc<Node>>>
Interior mutability allows mutation through shared reference
5
root.add_child(Rc::clone(&leaf))
Child reference creates shared ownership, both root and leaf keep each other alive