Reference Counting

Share Ownership of a Value with Rc

std book~rust~rc

The Rc<T>↗ type (for "Reference Counted") enables shared ownership of a value.

  • Rc maintains a reference count of the number of owners. You can create additional references to the data using the clone↗ method. Cloning an Rc only increments the reference count without duplicating the data. When the last owner goes out of scope, the data is automatically cleaned up (dropped).
  • If you need mutability, put a Cell↗ or RefCell↗ inside the Rc.
  • Rc automatically dereferences to T (via the Deref↗ trait), so you can call T's methods on a value of type Rc<T>.
  • Rc is commonly used in data structures, such as graphs and linked lists, where multiple nodes might need to share ownership of certain nodes or data.
  • Keep in mind that Rc is not thread-safe. For concurrent scenarios, you should use Arc↗, which provides similar functionality with thread safety.

The following example demonstrates common operations with Rc:

//! `Rc` is a smart pointer that allows multiple owners of the same data.
//!
//! `Rc` stands for "Reference Counted". It keeps track of the number of owners
//! of the data. When the last owner is dropped, the data is deallocated.
use std::rc::Rc;

fn main() {
    let a = Rc::new(vec![1.0, 2.0, 3.0]);
    // The two syntaxes below are equivalent for cloning an `Rc`:
    let b = a.clone();
    let c = Rc::clone(&a); // Preferred syntax.
    // `b` and `c` now both point to the same memory location as `a`.

    // Gets the number of (`Rc`) pointers to this allocation.
    assert_eq!(3, Rc::strong_count(&a));
    // Dropping one of the pointers decrements the (strong) reference count.
    drop(c);
    assert_eq!(2, Rc::strong_count(&a));
    // Note that the inherent functionality of `Rc` is implemented as associated
    // functions, not methods, thus the `Rc::rc_func(&rc)` calls.

    // `Rc` is a smart pointer, so we can dereference it.
    println!("{:?}", *a);
    // The `.` operator lets us call methods on the underlying vector.
    b.iter().for_each(|x| print!("{x} "));

    // We can get a mutable reference to the inner value, if there are NO OTHER
    // `Rc` (or `Weak`) pointers to the same allocation. Returns `None`
    // otherwise. Consider also `make_mut`, which will clone the inner value
    // when there are other `Rc` pointers.
    let mut y = Rc::new(4);
    *Rc::get_mut(&mut y).unwrap() = 5;
    assert_eq!(*y, 5);

    // We can also consume the `Rc` to return its inner value if the `Rc` has
    // exactly one strong reference. See also `Rc::into_inner`.
    let x = Rc::new(6);
    assert_eq!(Rc::try_unwrap(x), Ok(6));
}

Also of note: the Weak↗ (weak reference) type, typically obtained via Rc::downgrade, allows for non-owning (weak) references to the data. This can help prevent reference cycles that could lead to memory leaks.

Share Ownership of a Value with Interior Mutability via Rc and RefCell

std

Rc<T>↗ allows you to share data between multiple parts of your program but it doesn't allow unrestricted mutability by itself. If Rc<T> allowed multiple mutable references, it may violate Rust's borrowing rules: multiple mutable borrows to the same place can cause data races and inconsistencies.

If you need mutability inside an Rc, you can use RefCell<T>↗, which offers interior mutability: RefCell<T> enforces borrowing rules at runtime instead of compile-time. It allows mutable borrows to the inner data at runtime, but panics if borrowing rules are violated (again, only one mutable borrow is allowed at a time).

See Interior Mutability for more details.

  • Box.
  • Clone on Write.
  • Data Structures.
  • Interior Mutability.
  • Lifetimes.
  • Ownership and Borrowing.