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 (Atomic Reference Counted), 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 you 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 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.

Related Topics

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