Threads are great for CPU-bound parallelism, but they don't scale to tens of thousands of concurrent I/O operations — each OS thread costs memory and context-switching. async/await is Rust's answer for high-concurrency I/O: thousands of tasks on a handful of threads.
The problem async solves
A web server handling 10,000 connections mostly waits — for the network, the database, the disk. Dedicating an OS thread to each idle connection wastes memory. Async lets one thread juggle thousands of tasks: while one task waits for I/O, the thread runs another. It's concurrency without a thread per task.
Cooperative, not preemptive
Async tasks are cooperative: a task runs until it hits an .await point where it's waiting on something, then voluntarily yields the thread so another task can run. There's no OS preemption between awaits. This is why async excels at I/O-bound work (lots of waiting) and is the wrong tool for CPU-bound work (no natural yield points).
async is opt-in
Unlike some languages where everything is async, Rust keeps it opt-in and zero-cost: you only pay for async where you use it, and synchronous code stays simple. Most programs don't need async at all. You reach for it specifically when you have many concurrent I/O operations to manage.