Skip to content

Q041. Why does Arc not automatically make T thread-safe?

Short Answer

Arc<T> makes shared ownership bookkeeping thread-safe; it does not change the synchronization properties of T. An Arc<T> can be sent or shared between threads only when T satisfies the required Send and Sync bounds. For example, Arc<RefCell<T>> is not a thread-safe substitute for Arc<Mutex<T>> because RefCell uses non-atomic runtime borrow state.

Deep Dive

There are two separate concerns:

  1. Who keeps the value alive?
  2. Who may access or mutate it concurrently?

Arc answers the first with atomic reference counts. The inner type answers the second. Immutable data can often be shared as Arc<T>. A simple mutable value can use Arc<Mutex<T>> or Arc<RwLock<T>>. Independent numeric state may use atomics. State with one logical owner may be cleaner behind a channel.

Rust preserves these boundaries through auto traits. Roughly, Arc<T> is Send and Sync only when sharing T is safe. Wrapping an unsafe-to-share type does not erase that fact.

Internal Model

Arc::clone synchronizes only access to the reference-count fields. It does not lock the bytes of T, serialize its methods, or make non-atomic fields atomic.

The compiler checks Send and Sync at thread or task boundaries. There is no runtime mode in which Arc detects arbitrary data races inside T.

Example

rust
use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let counter = Arc::new(Mutex::new(0_u64));
    let worker_counter = Arc::clone(&counter);

    let worker = thread::spawn(move || {
        *worker_counter.lock().unwrap() += 1;
    });

    worker.join().unwrap();
    assert_eq!(*counter.lock().unwrap(), 1);

    // Arc<RefCell<u64>> would not satisfy the thread's Send requirements.
}

Common Mistakes

  • Saying atomic reference counting makes all operations on T atomic.
  • Wrapping RefCell<T> in Arc to share mutable state between threads.
  • Adding a lock without defining which invariant the lock protects.

Follow-ups

  • What do Send and Sync guarantee?
  • When is Arc<AtomicU64> preferable to Arc<Mutex<u64>>?
  • Why can a type be Send but not Sync?

References

  • Rust Standard Library
  • The Rust Programming Language