Skip to content

Arc and Mutex

In JavaScript, Workers do not share memory by default. Each Worker has its own heap. The only way to share mutable state is via SharedArrayBuffer with Atomics, which is low-level, error-prone, and rarely used in application code.

Rust supports shared mutable state across threads through two types that compose together:

  • Arc<T> — Atomically Reference Counted. A thread-safe smart pointer that lets multiple threads own the same heap-allocated value. When the last Arc clone is dropped, the value is freed.
  • Mutex<T> — Mutual exclusion lock. Wraps a value T and ensures only one thread can access the inner value at a time.

Together, Arc<Mutex<T>> is the standard Rust idiom for shared mutable state across threads.

Arc is Rust’s answer to “I need multiple owners across threads”. It is similar to Rc<T> (the single-threaded reference counter), but uses atomic operations for the reference count — safe to update from multiple threads simultaneously.

You clone an Arc to give another thread a handle to the same allocation. Cloning is cheap: it only increments an atomic integer.

Mutex<T> wraps a value and protects it with a lock. To access the inner T, you call .lock(), which:

  1. Blocks until no other thread holds the lock.
  2. Returns a MutexGuard<T> — a smart pointer that dereferences to &mut T.
  3. Automatically releases the lock when the MutexGuard is dropped (end of scope).

This is RAII locking — the same pattern Rust uses for memory: the resource is released automatically when the guard goes out of scope.

TypeScript
// TypeScript: no shared mutable state between Workers by default
// SharedArrayBuffer + Atomics is the low-level primitive
const sab = new SharedArrayBuffer(4);
const arr = new Int32Array(sab);
// Worker 1
Atomics.add(arr, 0, 1); // atomic increment
// Worker 2
Atomics.add(arr, 0, 1); // atomic increment
// Main thread
console.log(Atomics.load(arr, 0)); // 2
// In practice, most TS apps avoid shared memory between Workers
// and use message passing (postMessage) instead.
Rust
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
// Arc<Mutex<u32>>: shared, mutable counter across threads
let counter = Arc::new(Mutex::new(0u32));
let mut handles = vec![];
for _ in 0..5 {
let c = Arc::clone(&counter); // cheap: just increments atomic refcount
let h = thread::spawn(move || {
let mut val = c.lock().unwrap(); // acquire lock
*val += 1; // mutate inner value
// MutexGuard 'val' is dropped here — lock is released
});
handles.push(h);
}
for h in handles { h.join().unwrap(); }
// Only one owner remains — safe to read the final value
println!("final counter = {}", *counter.lock().unwrap()); // 5
}

A common pattern is to let each thread push a result into a shared Vec, then read the aggregate after all threads have joined.

TypeScript
// TypeScript: collect results with Promise.all
async function processAll(items: number[]): Promise<number[]> {
return Promise.all(items.map(item =>
Promise.resolve(item * item) // simulate async work
));
}
Rust
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let results: Arc<Mutex<Vec<u32>>> = Arc::new(Mutex::new(vec![]));
let items = vec![1u32, 2, 3, 4, 5];
let mut handles = vec![];
for item in items {
let r = Arc::clone(&results);
let h = thread::spawn(move || {
r.lock().unwrap().push(item * item);
});
handles.push(h);
}
for h in handles { h.join().unwrap(); }
let mut res = results.lock().unwrap().clone();
res.sort(); // sort for deterministic output
println!("squares: {:?}", res); // squares: [1, 4, 9, 16, 25]
}
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let counter = Arc::new(Mutex::new(0u32));
let mut handles = vec![];
for _ in 0..5 {
let c = Arc::clone(&counter);
let h = thread::spawn(move || {
let mut val = c.lock().unwrap();
*val += 1;
});
handles.push(h);
}
for h in handles {
h.join().unwrap();
}
println!("final counter = {}", *counter.lock().unwrap());
}
What does Arc stand for in Rust?
How do you release a Mutex lock in Rust?
When does a Mutex become 'poisoned'?
Why use Arc<Mutex<T>> instead of just Mutex<T>?