Implementing Interior Mutability in Single-Threaded Rust with RefCell
Owner: SnippetBot
Created: 2026-10-31 00:01:12
Size: 3.04 KB
Expires: Never
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
use std::cell::RefCell;
use std::rc::Rc; // Often used with RefCell to share ownership in single-threaded scenarios
// A 'Node' in a graph-like structure where its value might need to be
// mutated even when accessed through an immutable reference (e.g., during graph traversal).
#[derive(Debug)]
struct GraphNode {
id: u32,
value: RefCell<u32>, // Allows mutable access to `value` through an immutable `&GraphNode`
// In a real graph, you might have `children: Vec<Rc<GraphNode>>`
}
impl GraphNode {
fn new(id: u32, initial_value: u32) -> Self {
GraphNode {
id,
value: RefCell::new(initial_value),
}
}
// Increment the node's value. This method takes `&self` (an immutable reference),
// but can still mutate `self.value` thanks to `RefCell`.
fn increment_value(&self, amount: u32) {
// `borrow_mut()` gets a mutable reference to the inner value.
// This check happens at runtime. If another mutable borrow or any
// shared borrows exist, it will panic. This prevents data races in single-threaded contexts.
let mut val = self.value.borrow_mut(); // Runtime borrow check
*val += amount;
println!("Node {} value incremented to: {}", self.id, *val);
// `val` (the mutable borrow) is automatically dropped here, releasing the runtime lock.
}
// Get the current value. Takes `&self`, but safely accesses the inner value.
fn get_value(&self) -> u32 {
// `borrow()` gets an immutable reference to the inner value.
// Panics if a mutable borrow is currently active.
*self.value.borrow() // Runtime borrow check
}
}
fn main() {
println!("--- Demonstrating RefCell for interior mutability ---");
let node1 = GraphNode::new(1, 10);
println!("Initial node1: {:?}", node1);
node1.increment_value(5); // Mutating through an immutable reference
println!("Node1 after increment: {:?}", node1);
node1.increment_value(2);
println!("Node1 after another increment: {:?}", node1);
// Example of a runtime borrow error (uncomment to see panic)
/*
let mut_ref_1 = node1.value.borrow_mut();
println!("Acquired first mutable ref.");
// This will panic because `mut_ref_1` is still active
// let mut_ref_2 = node1.value.borrow_mut();
// println!("Acquired second mutable ref.");
*/
// Using Rc with RefCell to share mutable nodes in a single-threaded graph-like structure
let node_a = Rc::new(GraphNode::new(101, 100));
let node_b = Rc::clone(&node_a); // node_b is another `Rc` pointing to the same `GraphNode`
println!("
Shared Node A value: {}", node_a.get_value());
println!("Shared Node B value: {}", node_b.get_value());
node_a.increment_value(10); // Mutate through node_a's Rc handle
println!("Node A value after mutation: {}", node_a.get_value());
println!("Node B value (reflects A's mutation): {}", node_b.get_value());
// Without RefCell, if GraphNode::value was just u32, you couldn't
// mutate it via `&self` or via shared `Rc` references.
}