Code Example Template
async fn fetch() -> u32 { 42 }
let result = fetch().await;

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;