19.5 Rc<T>: Reference Counting for Shared Ownership

Rust’s ownership model ensures that each value has a single owner by default. However, some cases require multiple owners. Consider a graph where multiple edges point to the same node. Using Box<T> would mean copying data or enforcing a single exclusive owner, neither of which matches the intended semantics.

Rc<T> (Reference Counted) addresses this by allowing multiple pointers to share ownership of the same heap data. The data remains alive until all Rc<T> instances are dropped, at which point it’s freed automatically.

19.5.1 Why Rc<T> Is Needed

Without Rc<T>, cloning a Box<T> creates independent copies rather than shared ownership. For large, immutable data or complex, shared structures, this is inefficient and semantically incorrect. Rc<T> uses a reference count to share ownership without copying data.

19.5.2 How Rc<T> Works

Rc<T> stores both the data and a reference count in a single heap allocation. Each time you clone an Rc<T>, it increments the count. Dropping an Rc<T> decrements the count. When the count reaches zero, the data is freed.

Overhead:
Rc<T> adds a small runtime cost for maintaining the reference count. Still, this is typically more efficient than copying large data multiple times.

Single-Threaded Only:
Rc<T> is not thread-safe. For cross-thread sharing, use Arc<T>.

Immutability:
Rc<T> enables shared ownership but not shared mutability. To mutate shared data, combine Rc<T> with interior mutability tools like RefCell<T>.

Example: Graph Nodes

use std::rc::Rc;

#[derive(Debug)]
struct Node {
    value: i32,
}

fn main() {
    let node = Rc::new(Node { value: 42 });
    let edge1 = Rc::clone(&node);
    let edge2 = Rc::clone(&node);

    println!("Node via edge1: {:?}", edge1);
    println!("Node via edge2: {:?}", edge2);
    println!("Reference count: {}", Rc::strong_count(&node));
}

19.5.3 Limitations and Trade-Offs

  • Runtime overhead for reference counting.
  • Not thread-safe; use Arc<T> if you need multi-threaded shared ownership.
  • Immutability enforced at the type level unless combined with interior mutability types.