Interior mutability lets you change data inside an otherwise immutable structure when ordinary mutable references are too restrictive. It is an escape hatch, not a way around Rust’s safety rules. Use it for a specific data-structure need, not as a routine response to borrow-checker errors. section 5.6
Choose the wrapper for the access you need:
- Use RefCell when you need to borrow references; Cell moves values in and out. Both are for single-threaded use. section 5.6 section 5.10
- For shared ownership across threads, use Arc with Mutex or RwLock for internal mutability. Prefer Rc when atomic reference counting is unnecessary: Arc’s atomic operations cost more. section 5.10 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
}
}
}
The doubly linked list’s `Rc<RefCell<ListItem<T>>>` alias combines shared ownership with access to mutable list items. Its links use that alias, while each item’s data is boxed. section 5.6


