liveBook · Manning

Refcell

As explained in Code Like a Pro in Rust

RefCell is a Rust standard-library type that provides runtime-checked borrowing and interior mutability for single-threaded code.

`RefCell<T>` enables interior mutability when the borrow checker cannot establish that access to data inside a shared structure is safe. It allows references to be borrowed and checks borrowing at runtime. Use it for a specific need, not as a routine way around the borrow checker. section 5.6

Shared nodes in a doubly linked list

A single-threaded list can define `type ItemRef<T> = Rc<RefCell<ListItem<T>>>;` and create its head with `Rc::new(RefCell::new(ListItem::new(data)))`. `Rc` provides shared ownership; `RefCell` permits mutable access within the shared node. 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 listing shows how `Rc<RefCell<ListItem<T>>>` fits into the list’s node links and head. section 5.6

Choose the smallest mechanism that meets the access requirement:

  • Prefer `RefCell` over `Cell` when you need to borrow references; `Cell` moves values in and out. section 5.6
  • Keep `RefCell` to single-threaded use. For interior mutability across threads, use `Mutex` or `RwLock`, typically with `Arc`. section 5.6

How Code Like a Pro in Rust covers it

How deep it goes
Explains when to use RefCell and shows its placement in a list; defers concurrency and does not detail runtime borrow checks.
Examples
Uses a doubly linked list whose node references have the type `Rc<RefCell<ListItem<T>>>`.
What it assumes
Assumes familiarity with Rust references and the borrow checker; introduces interior mutability and RefCell for the shared-ownership case.
What you can do after
Apply it via Rust’s `RefCell` and `Rc` to structure mutable, shared nodes in a single-threaded list.
How it connects
Connects interior mutability with the borrow checker, Rc, Cell, Mutex, RwLock, and Arc.

Also explained in

Compare how 3 books explain refcell →

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.