// Example 108: Rc<T> for Shared Ownership
//
// Rc<T> = Reference Counted pointer. Multiple owners of the same data.
// Like OCaml's default GC sharing, but explicit and single-threaded.
use std::rc::Rc;
// Approach 1: Shared tree nodes with Rc
#[derive(Debug)]
enum Tree {
Leaf,
Node(Rc<Tree>, i32, Rc<Tree>),
}
fn tree_sum(t: &Tree) -> i32 {
match t {
Tree::Leaf => 0,
Tree::Node(l, v, r) => tree_sum(l) + v + tree_sum(r),
}
}
fn approach1() {
let shared = Rc::new(Tree::Node(
Rc::new(Tree::Leaf), 42, Rc::new(Tree::Leaf),
));
// Clone Rc = increment reference count (cheap!)
let tree1 = Tree::Node(Rc::clone(&shared), 1, Rc::new(Tree::Leaf));
let tree2 = Tree::Node(Rc::new(Tree::Leaf), 2, Rc::clone(&shared));
println!("Rc strong count: {}", Rc::strong_count(&shared));
assert_eq!(tree_sum(&tree1), 43);
assert_eq!(tree_sum(&tree2), 44);
println!("Tree1 sum: {}, Tree2 sum: {}", tree_sum(&tree1), tree_sum(&tree2));
}
// Approach 2: Shared data in multiple collections
fn approach2() {
let shared = Rc::new(vec![3, 4, 5]);
let collection_a = (vec![1, 2], Rc::clone(&shared));
let collection_b = (vec![10], Rc::clone(&shared));
assert_eq!(Rc::strong_count(&shared), 3); // original + 2 clones
println!("Shared vec: {:?}, refs: {}", shared, Rc::strong_count(&shared));
println!("A: {:?} + {:?}", collection_a.0, collection_a.1);
println!("B: {:?} + {:?}", collection_b.0, collection_b.1);
}
// Approach 3: DAG with Rc
#[derive(Debug)]
struct DagNode {
id: i32,
children: Vec<Rc<DagNode>>,
}
fn approach3() {
let shared = Rc::new(DagNode { id: 3, children: vec![] });
let a = Rc::new(DagNode { id: 1, children: vec![Rc::clone(&shared)] });
let b = Rc::new(DagNode { id: 2, children: vec![Rc::clone(&shared)] });
let root = DagNode { id: 0, children: vec![a, b] };
assert_eq!(root.id, 0);
assert_eq!(root.children.len(), 2);
assert_eq!(Rc::strong_count(&shared), 3); // a, b, and shared
println!("DAG root {} has {} children, shared refs: {}",
root.id, root.children.len(), Rc::strong_count(&shared));
}
fn main() {
println!("=== Approach 1: Shared Tree Nodes ===");
approach1();
println!("\n=== Approach 2: Shared Collections ===");
approach2();
println!("\n=== Approach 3: DAG ===");
approach3();
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_rc_clone_is_cheap() {
let data = Rc::new(String::from("hello"));
let clone = Rc::clone(&data);
assert_eq!(*data, *clone);
assert_eq!(Rc::strong_count(&data), 2);
}
#[test]
fn test_rc_drop_decrements() {
let data = Rc::new(42);
let clone = Rc::clone(&data);
assert_eq!(Rc::strong_count(&data), 2);
drop(clone);
assert_eq!(Rc::strong_count(&data), 1);
}
#[test]
fn test_shared_tree() {
let leaf = Rc::new(Tree::Leaf);
let node = Tree::Node(Rc::clone(&leaf), 10, Rc::clone(&leaf));
assert_eq!(tree_sum(&node), 10);
}
#[test]
fn test_dag_shared_child() {
let child = Rc::new(DagNode { id: 1, children: vec![] });
let p1 = DagNode { id: 2, children: vec![Rc::clone(&child)] };
let p2 = DagNode { id: 3, children: vec![Rc::clone(&child)] };
assert_eq!(Rc::strong_count(&child), 3);
drop(p1);
assert_eq!(Rc::strong_count(&child), 2);
drop(p2);
assert_eq!(Rc::strong_count(&child), 1);
}
}
(* Example 108: Rc<T> — OCaml Default Sharing → Rust Explicit Rc *)
(* In OCaml, all values are implicitly reference-counted/GC-managed.
Multiple bindings to the same value is the default behavior. *)
(* Approach 1: Shared tree nodes *)
type 'a tree = Leaf | Node of 'a tree * 'a * 'a tree
let shared_subtree () =
let shared = Node (Leaf, 42, Leaf) in
let tree1 = Node (shared, 1, Leaf) in
let tree2 = Node (Leaf, 2, shared) in
(* shared is used in both trees — GC keeps it alive *)
(tree1, tree2)
let rec tree_sum = function
| Leaf -> 0
| Node (l, v, r) -> tree_sum l + v + tree_sum r
let approach1 () =
let (t1, t2) = shared_subtree () in
let s1 = tree_sum t1 in
let s2 = tree_sum t2 in
Printf.printf "Tree1 sum: %d, Tree2 sum: %d\n" s1 s2;
assert (s1 = 43);
assert (s2 = 44)
(* Approach 2: Shared list nodes — cons sharing *)
let approach2 () =
let shared_tail = [3; 4; 5] in
let list_a = 1 :: 2 :: shared_tail in
let list_b = 10 :: shared_tail in
assert (List.length list_a = 5);
assert (List.length list_b = 4);
Printf.printf "list_a length: %d, list_b length: %d\n"
(List.length list_a) (List.length list_b)
(* Approach 3: DAG (directed acyclic graph) via sharing *)
type node = { id : int; children : node list }
let approach3 () =
let shared = { id = 3; children = [] } in
let a = { id = 1; children = [shared] } in
let b = { id = 2; children = [shared] } in
let root = { id = 0; children = [a; b] } in
assert (root.id = 0);
assert (List.length root.children = 2);
Printf.printf "DAG root %d has %d children\n" root.id (List.length root.children)
let () =
approach1 ();
approach2 ();
approach3 ();
Printf.printf "✓ All tests passed\n"