Reference Counting
Recipe | Crates |
---|---|
Share Ownership of a Value with Rc | |
Interior Mutability with Rc and RefCell | |
Share Ownership of a Value with Rc |
Share Ownership of a Value with 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 theclone
⮳ method. Cloning anRc
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
orRefCell
inside theRc
. Rc
automatically dereferences toT
(via theDeref
⮳ trait), so you can callT
's methods on a value of typeRc<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 useArc
(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
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.