Course contentsShow
← Rust
Lesson 1085 of 1,91010. Concurrency PrimitivesFree lesson

Safe Shared State via Arc

You will implement thread-safe reference counting to share heap-allocated data across multiple threads.

In this lesson, you will learn how to safely share read-only data across multiple threads using the Arc<T> (Atomic Reference Counter) type.

The Challenge of Shared Ownership

In Rust, the ownership model dictates that a value can have only one owner. When you spawn a thread using std::thread::spawn, the compiler requires that any data moved into the closure has a 'static lifetime or is fully owned by the thread. If you want two threads to read the same piece of heap-allocated data simultaneously, standard ownership won't suffice. You cannot simply pass a reference, because the compiler cannot guarantee that the main thread won't drop the data while another thread is still reading it.

Enter Arc<T>

Arc<T> stands for Atomic Reference Counter. It allows you to wrap a piece of data so that it can have multiple owners. Internally, Arc keeps track of how many references exist to its inner value. Every time you clone an Arc, you aren't cloning the data itself; you are merely incrementing an atomic counter on the heap.

When a thread finishes and the Arc goes out of scope, the counter decrements. Only when the count reaches zero is the underlying data finally cleaned up. This makes Arc the perfect bridge for cross-thread data access.

Practical Implementation

To share data, you wrap it in an Arc, clone the pointer for each thread, and move those clones into the spawned closures:

use std::sync::Arc;
use std::thread;

fn main() {
    let data = Arc::new(vec![1, 2, 3]);

    let mut handles = vec![];

    for i in 0..3 {
        let thread_data = Arc::clone(&data); // Increment reference count
        let handle = thread::spawn(move || {
            println!("Thread {} sees: {:?}", i, thread_data);
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }
}

Because Arc uses atomic operations to update its reference count, it is thread-safe and prevents data races during the increment/decrement process. Note that Arc provides shared access to immutable data; if you need to mutate the state across threads, you would need to pair this with interior mutability (such as Mutex<T>), which we will explore in later sections.

Key Takeaway: Arc<T> uses atomic reference counting to safely manage the lifetime of heap-allocated data, allowing multiple threads to own and access the same resource without manual lifetime management.