liveBook · Manning

Rc

As explained in Code Like a Pro in Rust

Rc is Rust's single-threaded reference-counted smart-pointer type for shared ownership of a value.

`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

How Code Like a Pro in Rust covers it

How deep it goes
Explains count increments and decrements, then shows an Rc-based node representation; leaves raw-pointer implementation as an exercise.
Examples
Uses a doubly linked list whose node references are `Rc<RefCell<ListItem<T>>>`.
What it assumes
Assumes Rust pointer and trait familiarity while explaining the reference-counting rule before applying it.
What you can do after
Apply it via `Rc` for single-threaded shared ownership, pairing it with `RefCell` when shared nodes need mutation.
How it connects
Connects Rc to Arc, Send, Sync, RefCell, Box, Clone, and clone-on-write.

Also explained in

Compare how 2 books explain Rc →

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.