Concurrency Fundamentals

Rust's Concurrency Model

Rust's concurrency model—often called fearless concurrency—enforces thread safety at compile time using the exact same ownership, borrowing, and type-system rules that prevent dangling pointers and aliasing bugs in single-threaded code, while keeping the language runtime minimal (1:1 native OS threads in std and zero-allocation state machines for async).

Language-Level Foundations vs. Library-Level Primitives

Rather than baking a heavy green-thread runtime or garbage collector into the language binary, Rust puts only a tiny set of concurrency rules in the core language and implements the rest in the standard library and ecosystem crates:

+-------------------------------------------------------------------+
|  Language Core (Enforced by `rustc` Type & Borrow Checker)        |
|    - Ownership & Move semantics (`move ||`)                       |
|    - Aliasing invariant (`many &T` XOR `one &mut T`)              |
|    - Auto-traits `Send` and `Sync`                                |
|    - Lifetimes (`'static` on `thread::spawn`, `'env` on `scope`)  |
|    - `async` / `.await` state-machine lowering (`Future` trait)   |
+---------------------------------+---------------------------------+
                                  |
                                  v
+-------------------------------------------------------------------+
|  Standard Library (`std::thread`, `std::sync`) & Ecosystem        |
|    - 1:1 OS Threads (`thread::spawn`, `thread::scope`)            |
|    - Shared Ownership (`Arc<T>`, `Weak<T>`)                       |
|    - Synchronization (`Mutex<T>`, `RwLock<T>`, `Condvar`, `Barrier`)|
|    - Lock-Free Atomics (`AtomicBool`, `AtomicUsize`, `Ordering`)  |
|    - Message Channels (`std::sync::mpsc`)                         |
+-------------------------------------------------------------------+

Why 1:1 OS Threads Are Used in the Standard Library

Early pre-1.0 versions of Rust experimented with an M:N green-threading runtime, which required every binary to bundle a heavy scheduler and complicated C FFI stack management. Rust 1.0 and all subsequent editions standardized on 1:1 native OS threads in std::thread:

  • Every std::thread::spawn maps directly to one operating-system thread (pthread on Unix, Win32 thread on Windows) with zero runtime overhead when threads are not used and seamless zero-cost C FFI interoperability.
  • When an application needs lightweight M:N task multiplexing for massive I/O concurrency, it opts into async/.await with an executor crate (tokio, smol, embassy) tailored to its workload.

How Ownership Prevents Classic Concurrency Bugs at Compile Time

Classic Concurrency Bug in C/C++ How Rust Prevents It at Compile Time
Use-after-free by a background thread (main thread frees stack frame while background thread holds pointer) thread::spawn requires F: 'static (forcing move or Arc<T>), or thread::scope joins all threads before the stack frame exits (E0373 / E0597)
Unprotected concurrent mutation (Data Race) Shared references &T are immutable; mutating through &T requires a Sync wrapper (Mutex<T>, RwLock<T>, Atomic*) (E0596 / E0277)
Accessing a mutex-protected variable without locking Mutex<T> owns T inside the lock; T can only be accessed through the MutexGuard<'_, T> returned by .lock()
Forgetting to unlock a mutex on early return MutexGuard<'_, T> unlocks automatically in its Drop destructor
Sending a non-thread-safe handle (Rc<T>, RefCell<T>) across threads Rejected by Send / Sync trait bounds on thread::spawn and thread::scope (E0277)
use std::thread;

fn main() {
    let mut results = [0_u32; 4];

    // `thread::scope` + `chunks_mut` lets 4 threads mutate disjoint slices
    // of a stack array in parallel with ZERO locks and ZERO heap allocations!
    thread::scope(|s| {
        for (idx, slot) in results.iter_mut().enumerate() {
            s.spawn(move || {
                *slot = ((idx as u32) + 1) * 10;
            });
        }
    });

    println!("parallel disjoint mutations: {:?}", results);
}
parallel disjoint mutations: [10, 20, 30, 40]