Concept · Rust
Lazy futures and async runtimes
A Rust future is a state machine that does nothing until polled; a runtime such as Tokio polls it and wakes it when I/O is ready.
An async fn in Rust returns a future: a value describing work that hasn't started. Nothing runs until the future is awaited or handed to a runtime. That's the opposite of a JavaScript promise, which starts running as soon as it's created.
let fut = fetch_user(42); // nothing happens yet
let user = fut.await; // now it runsHow it runs
The compiler turns each async fn into a state machine with one state per .await. A runtime polls it: the future either finishes (Poll::Ready) or registers a waker with the I/O reactor and returns Poll::Pending. When the socket becomes readable, the reactor calls the waker and the executor polls the future again.
The standard library defines the Future trait but ships no runtime. Tokio is the de facto choice for servers: a multi-threaded executor, an epoll/kqueue reactor, timers, and async channels and locks. Thousands of tasks share a few OS threads, which suits workloads that mostly wait on the network — the same idea as the JavaScript event loop, across several threads.
Consequences
- Cancellation is dropping.
select!andtimeoutcancel a future by not polling it again. No token is needed, unlike Go's context, but work done before the last completed.awaitstays done, so cancellation safety matters. - Never block. A blocking call (
std::thread::sleep, synchronous I/O, heavy CPU work) holds a worker thread and stalls every task queued on it. Use async APIs, orspawn_blocking. - Spawned futures must be
Send + 'static. Anything held across an.awaitis part of the state machine, so anRcor astdmutex guard there makes the task impossible to move between threads. See Send and Sync.
The async Rust lesson covers Tokio, join!, spawn, select! and timeouts.