Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Rc under Multithreading

Goal of This Episode

Understand why Rc can’t cross Threads at all — neither Send nor Sync.

Concept

Rc Is Not Sync

Rc’s reference count, like RefCell’s borrow count, is an ordinary integer — no atomic operations. If several Threads clone or drop through &Rc<T> simultaneously, the count’s increments and decrements can trample each other, corrupting the count — releasing the data early, or never releasing it.

So Rc isn’t Sync, for the same reason as RefCell.

Rc Isn’t Even Send

Last episode said RefCell is Send, since after a move one Thread alone owns it. Rc is different.

Rc’s whole design is multiple Rcs pointing at one piece of data. Move one Rc to another Thread, and its clones may remain on the original Thread. Both sides operating on the reference count simultaneously can wreck the counter.

The problem isn’t the move itself, but that after the move, two Threads still share one counter.

use std::rc::Rc;

fn main() {
    let a = Rc::new(42);
    let b = a.clone(); // a and b share the data and the counter

    // If a moved to another thread,
    // b would remain on the original — both sides touching the counter at once, boom
    std::thread::spawn(move || {
        println!("{}", a);
    });
    // Compile error! Rc<i32> is not Send
}

Rc Can’t Cross Threads, Period

Rc is neither Send nor Sync. It can’t move to other Threads, nor share references among them. Sharing data across Threads takes a different tool.

Example Code

use std::rc::Rc;
use std::thread;

fn main() {
    // Rc works normally in a single thread
    let a = Rc::new(String::from("hello"));
    let b = a.clone();
    println!("a = {}, b = {}", a, b);
    println!("Count = {}", Rc::strong_count(&a));

    // But it can't cross threads — the following won't compile:

    // let data = Rc::new(42);
    // thread::spawn(move || {
    //     println!("{}", data);
    // });
    // Compile error: Rc<i32> is not Send

    println!("Rc is single-threaded only");
}

Recap

  • Rc’s reference count is an ordinary integer, not atomic — so not Sync.
  • Rc isn’t even Send: after moving an Rc to another Thread, its clones may remain behind, and both sides touching the counter at once breaks it.
  • In short: Rc cannot cross Threads at all.