liveBook · Manning

Interior mutability

As explained in Code Like a Pro in Rust

Interior mutability is a programming pattern in which a value permits mutation through a shared or otherwise nonmutable handle while enforcing its safety rules at runtime or through synchronization.

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

How Code Like a Pro in Rust covers it

How deep it goes
Distinguishes Cell from RefCell and identifies threaded alternatives, but stops short of detailing their borrowing mechanisms.
Examples
Uses a doubly linked list with Rc<RefCell<ListItem<T>>> links and boxed data.
What it assumes
Assumes familiarity with Rust’s borrow checker and mutable references while introducing Cell and RefCell as less common types.
What you can do after
Apply it via RefCell with Rc for suitable single-threaded data structures, or Arc with synchronization for multithreaded access.
How it connects
Connects the borrow checker and mutable references to shared ownership, reference counting, Mutex, and RwLock.

Also explained in

Compare how 3 books explain interior mutability →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Code Like a Pro in Rust. The section links point to the parts of the book it draws on.