In Rust, ownership rules dictate that each value has a single owner, and when the owner goes out of scope, the value is dropped. But what if you need multiple parts of your program to share ownership of the same data? This is where Rc<T> (Reference Counted) comes in.
Rc<T> is a smart pointer that enables shared ownership of a value. It keeps track of how many references (or "owners") exist to a value, and only deallocates the value when the last reference is dropped. This is called reference counting.
Rc<T> is useful in scenarios like:
Important: Rc<T> is only for single-threaded scenarios. For multi-threaded code, use Arc<T> (Atomic Reference Counted) instead.
Rc::new(value) - Create a new reference-counted pointerRc::clone(&rc) - Create a new reference (increments count, doesn't deep clone)Rc::strong_count(&rc) - Get the current number of strong referencesRc::weak_count(&rc) - Get the current number of weak referencesRc::downgrade(&rc) - Create a Weak<T> reference that doesn't prevent deallocationweak.upgrade() - Try to convert a Weak<T> back to Rc<T> (returns Option)Implement the following types and functions to demonstrate Rc<T> patterns:
create_shared<T>(value: T) -> Rc<T> - Create a new reference-counted valueclone_shared<T>(rc: &Rc<T>) -> Rc<T> - Clone a reference (increment count)get_strong_count<T>(rc: &Rc<T>) -> usize - Get the strong reference countget_value<T: Clone>(rc: &Rc<T>) -> T - Get a clone of the inner valueSharedBuffer - A buffer that can be shared across multiple owners:
new(data: Vec<u8>) -> Rc<Self> - Create a new shared bufferlen(&self) -> usize - Get the buffer lengthget(&self, index: usize) -> Option<u8> - Get a byte at indexas_slice(&self) -> &[u8] - Get the buffer as a slicecreate_weak<T>(rc: &Rc<T>) -> Weak<T> - Create a weak referenceupgrade_weak<T>(weak: &Weak<T>) -> Option<Rc<T>> - Try to upgrade a weak referenceget_weak_count<T>(rc: &Rc<T>) -> usize - Get the weak reference countNode<T> - A graph node that can have multiple parents sharing child nodes:
new(value: T) -> Rc<Self> - Create a new nodevalue(&self) -> &T - Get reference to the valueadd_child(&mut self, child: Rc<Node<T>>) - Add a child nodechildren(&self) -> &[Rc<Node<T>>] - Get all childrenObservable<T> - A value with weak observers:
new(value: T) -> Self - Create observable with initial valueget(&self) -> &T - Get reference to valueset(&mut self, value: T) - Set new valuesubscribe(&mut self, observer: Rc<dyn Observer<T>>) - Add an observer (stored as Weak)notify(&self) - Notify all live observersObserver<T> trait:
on_update(&self, value: &T) - Called when observable changesuse rc_reference_counting::*;
use std::rc::Rc;
// Basic Rc operations
let shared = create_shared(42);
assert_eq!(get_strong_count(&shared), 1);
let cloned = clone_shared(&shared);
assert_eq!(get_strong_count(&shared), 2);
assert_eq!(get_value(&shared), 42);
// Shared buffer
let buffer = SharedBuffer::new(vec![1, 2, 3, 4, 5]);
assert_eq!(buffer.len(), 5);
assert_eq!(buffer.get(2), Some(3));
// Weak references
let strong = create_shared(String::from("hello"));
let weak = create_weak(&strong);
assert_eq!(get_weak_count(&strong), 1);
if let Some(upgraded) = upgrade_weak(&weak) {
assert_eq!(*upgraded, "hello");
}
drop(strong);
// Value was deallocated
assert!(upgrade_weak(&weak).is_none());Rc::new() to wrap values for shared ownershipRc::clone(&rc) is cheap - it only increments a counter, doesn't copy dataRc::downgrade() creates a Weak<T> that doesn't count toward keeping the value aliveWeak::upgrade() returns Option<Rc<T>> - None if the value was already droppedNode with mutable children, you might need RefCell inside the node (interior mutability)Rc<T> only works in single-threaded code!