In this lesson, you will learn how to share ownership of data across multiple threads safely using Arc.
The Problem with Rc
In previous lessons, you learned about Rc (Reference Counted) pointers. Rc allows multiple parts of your program to "own" the same piece of data. However, Rc is not thread-safe. If you try to move an Rc into a new thread, the compiler will stop you. Why? Because the internal reference count in Rc is updated using standard, non-atomic operations. If two threads try to increment or decrement that count at the exact same time, the math could go wrong, leading to memory corruption.
Enter Arc
To share data across threads, Rust provides Arc, which stands for Atomically Reference Counted. The "atomic" part is the secret sauce. An atomic operation is an operation that is guaranteed to finish completely without being interrupted by other threads.
When you use an Arc, the reference counting operations are handled by the CPU at a hardware level. This ensures that even if ten threads drop their reference to the data at the exact same moment, the total count remains perfectly accurate.
Example: Sharing State
Imagine you have a configuration object that multiple worker threads need to read. You wrap it in an Arc:
use std::sync::Arc;
use std::thread;
let data = Arc::new(vec![1, 2, 3]);
for i in 0..3 {
let thread_data = Arc::clone(&data);
thread::spawn(move || {
println!("Thread {} sees: {:?}", i, thread_data);
});
}
In this code, Arc::clone creates a new pointer that points to the same memory location, and it updates the atomic counter. When each thread finishes, the Arc drops, and the counter decrements atomically. When the last thread finishes, the Arc sees the count hit zero and cleans up the memory.
This allows you to safely move ownership of the pointer into threads while keeping the underlying data shared and valid.
Key Takeaway: Use Arc instead of Rc when you need to share ownership of data across multiple threads, as it uses atomic operations to keep reference counts thread-safe.