Deep-Dive System Documentation
Cooperative Concurrency: Futures & Async/Await
Unlike OS threads (which are preemptive and carry heavy context-switching overheads), Rust's asynchronous model is cooperative and built entirely around Futures. An asynchronous function does not run immediately; instead, it compiles into a high-performance state machine representing work to be completed.
The Future Trait
At the core of async Rust is the Future trait in std::future:
pub trait Future {
type Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
}
Poll::Pending: The task is waiting on an I/O event. It yields control back to the runner thread.Poll::Ready(val): The task is fully complete, yielding its computed result.
The Role of the Async Runtime
Rust's standard library does not include an executor or run-loop runtime. It only defines the abstractions. To run asynchronous functions, you must use an external executor library like Tokio or async-std:
// #[tokio::main] sets up the multithreaded reactor runtime and drives the future
#[tokio::main]
async fn main() {
let handle = tokio::spawn(async {
println!("Running on background worker thread!");
});
handle.await.unwrap();
}
Async fn under the hood
When you declare a function as async fn, the compiler automatically rewrites the signature to return an anonymous type implementing Future:
// Written code:
async fn get_data() -> u32 { 42 }
// Compiled equivalent:
fn get_data() -> impl Future<Output = u32> {
async { 42 }
}
.await vs Thread Sleep
When you call std::thread::sleep, you block the entire OS thread, freezing any other tasks scheduled on it. When you call .await on a future, you yield the thread. The executor pauses your task, stores its exact register state in a compiler-generated struct, and immediately switches to execute other ready futures on the same thread.
// Blocks the OS thread completely
std::thread::sleep(std::duration::Duration::from_secs(1));
// Yields the thread cooperatively to let other async tasks run
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
Low-Level OS Integration
Modern executors map futures to highly efficient, non-blocking OS system primitives. Instead of blocking threads on socket reads, they register sockets on event queues like epoll (Linux), kqueue (macOS/BSD), or IOCP (Windows) and poll the future only when the kernel signals that the network interface is ready.
Quick Reference Guide
use std::future::Future;
async fn work() -> &'static str { 'Done' }
// Under the hood, async returns impl Future<Output = &'static str>
let fut = work();
let res = fut.await;