`Rc` is Rust’s reference-counted smart pointer for shared ownership within one thread. Each new pointer copy increases a count; destroying a copy decreases it. When the count reaches zero, the value’s memory can be released. section 5.6
Choose the pointer and its companions according to the ownership problem:
- Use `Rc` when several owners need the same value in one thread. It does not implement `Send` or `Sync`, so it cannot serve as a cross-thread shared pointer. section 5.6
- Use `Arc` when ownership must cross threads. Its atomic counters cost more than ordinary arithmetic, so do not choose it solely as a default replacement for `Rc`. section 5.6
- Pair `Rc` with `RefCell` when shared ownership also requires interior mutability in a single-threaded structure. section 5.10 section 5.6
Shared, mutable list nodes
A doubly linked list can define `type ItemRef<T> = Rc<RefCell<ListItem<T>>>;`. Its nodes hold optional references to neighboring nodes, while `Box<T>` holds each node’s data. The type alias makes the combination of shared ownership and interior mutability explicit at each link. section 5.6
Listing 5.6 Code of a doubly linked list using Rc, RefCell, and Box
use std::cell::RefCell;
use std::rc::Rc;
struct ListItem<T> {
prev: Option<ItemRef<T>>, #1
data: Box<T>, #2
next: Option<ItemRef<T>>,
}
type ItemRef<T> = Rc<RefCell<ListItem<T>>>; #3
struct DoublyLinkedList<T> {
head: ItemRef<T>,
}
impl<T> ListItem<T> {
fn new(data: T) -> Self {
ListItem {
prev: None,
data: Box::new(data), #4
next: None,
}
}
fn data(&self) -> &T {
self.data.as_ref()
}
}
impl<T> DoublyLinkedList<T> {
fn new(data: T) -> Self {
DoublyLinkedList {
head: Rc::new(RefCell::new(ListItem::new(data))),
}
}
fn append(&mut self, data: T) {
let tail = Self::find_tail(self.head.clone()); #5
let new_item = Rc::new(RefCell::new(ListItem::new(data))); #6
new_item.borrow_mut().prev = Some(tail.clone()); #7
tail.borrow_mut().next = Some(new_item); #8
}
fn head(&self) -> ItemRef<T> {
self.head.clone()
}
fn tail(&self) -> ItemRef<T> {
Self::find_tail(self.head())
}
fn find_tail(item: ItemRef<T>) -> ItemRef<T> {
if let Some(next) = &item.borrow().next { #9
Self::find_tail(next.clone()) #10
} else {
item.clone() #11
}
}
}
Inspect the node fields and `ItemRef` alias to see where `Rc`, `RefCell`, and `Box` each enter the list representation. section 5.6
For an alternative to changing shared data in place, `Rc::make_mut()` supports clone-on-write. This connects `Rc` to the `Clone` trait: changes can produce a cloned value while the original remains intact. Implementing a reference-counted pointer yourself is a separate, more advanced exercise requiring raw unsafe pointers. section 5.7 section 5.6

