liveBook · Manning

Reference counting

As explained in Code Like a Pro in Rust

Reference counting is a memory-management technique that tracks the number of references to an object and reclaims it when that count reaches zero.

Reference counting supports shared ownership when one owner is not enough—for example, when multiple structures need to point to the same data. A count tracks copies of a pointer: making a copy increments it, destroying a copy decrements it, and reaching zero allows the memory to be released. section 5.6

Choose the pointer for the sharing context:

  • Use Rc for single-threaded shared ownership. section 5.6
  • Use Arc when ownership must be shared across threads. 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 defines its item reference as `Rc<RefCell<ListItem<T>>>` and stores each item’s data in a `Box<T>`. It shows reference counting used alongside other pointer types in a concrete structure. section 5.6

Prefer the provided pointer types to writing a reference-counted pointer solely to share data. Implementing one yourself is a more demanding exercise that requires raw, unsafe pointers. section 5.6

How Code Like a Pro in Rust covers it

How deep it goes
Explains count changes and zero-count release, but leaves custom pointer implementation as an unsafe-pointer exercise.
Examples
Shows a doubly linked list using `Rc<RefCell<ListItem<T>>>` for item references and `Box<T>` for data.
What it assumes
Assumes familiarity with Rust ownership and Box while explaining the reference-counting mechanism directly.
What you can do after
Apply it via Rc for single-threaded sharing or Arc for sharing across threads.
How it connects
Connects shared ownership with Box, Rc, Arc, RefCell, and doubly linked lists.

Also explained in

Compare how 4 books explain reference counting →

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.