C.W.K.
Stream
Lesson 05 of 05 · published

Arc — Thread-Safe Sharing

~11 min · smart-pointers, arc, send-sync, concurrency

Level 0Rust Curious
0 XP0/80 lessons0/19 achievements
0/100 XP to next level100 XP to go0% complete

Rc and RefCell work within a single thread. The moment you cross thread boundaries, you need their thread-safe counterparts: Arc and Mutex. This lesson introduces Arc and bridges into the Concurrency track.

Arc: atomic reference counting

Arc<T> is Rc<T> with an atomic count — safe to clone and drop from multiple threads simultaneously. The 'A' is for atomic: incrementing and decrementing the count uses atomic CPU operations, so two threads can't corrupt it. The API is identical to Rc; only the thread-safety (and a tiny performance cost) differ.

Why not always use Arc?

Atomic operations cost a little more than plain increments. Rc exists for the common single-threaded case where you don't need to pay that. And the compiler enforces the choice: Rc is not Send (can't cross threads), so if you try to share an Rc between threads it won't compile — and the error points you to Arc. You can't pick wrong.

Rc/RefCell for one thread; Arc/Mutex across threads. The pattern is parallel: Rc<RefCell<T>> for shared-mutable single-threaded, Arc<Mutex<T>> for shared-mutable across threads. The compiler won't let you use the single-threaded versions across threads — Send and Sync (next track) enforce it.

The bridge to fearless concurrency

Arc is how you share ownership of data with a spawned thread. Combined with Mutex for safe mutation, Arc<Mutex<T>> is the bread-and-butter of shared state in concurrent Rust. The next track makes that concrete — and shows why the compiler can promise 'no data races' even here.

Code

Sharing read-only data across threads with Arc·rust
use std::sync::Arc;
use std::thread;

fn main() {
    // Arc: shareable across threads (atomic refcount)
    let data = Arc::new(vec![1, 2, 3]);

    let mut handles = vec![];
    for id in 0..3 {
        let data = Arc::clone(&data); // each thread gets its own handle
        handles.push(thread::spawn(move || {
            println!("thread {id} sees {} items", data.len());
        }));
    }
    for h in handles {
        h.join().unwrap();
    }
}

External links

Exercise

Share a read-only Arc<Vec<i32>> across three spawned threads, each printing the sum of a different slice of it. Then try replacing Arc with Rc and read the compiler error about Send. What is the compiler protecting you from?
Hint
Rc's non-atomic count could be corrupted by two threads incrementing it at once — a data race. Because Rc isn't Send, the compiler refuses to move it into a thread, steering you to the atomic Arc. The bug is impossible, not just discouraged.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.