Async and Concurrency in Rust

Rust has two main ways to do things in parallel: threads and async. They solve different problems.

Threads are for CPU-bound work. You have computation that can run in parallel, and you want to use multiple cores. Rust threads are OS threads - real parallelism.

use std::thread;

let handle = thread::spawn(|| {
    // runs on a separate OS thread
    expensive_computation()
});

let result = handle.join().unwrap();

Async is for I/O-bound work. You’re waiting on network, disk, timers - things where the CPU is idle most of the time. Instead of blocking a thread while waiting, you yield control so other tasks can run.

async fn fetch_data() -> Data {
    let response = client.get(url).await;  // yields while waiting
    response.json().await
}

The difference matters for resource usage. Threads are expensive - each one needs stack space and OS resources. You can have thousands of async tasks on a single thread, but thousands of threads will bog down your system.

Rust’s async is a bit different from other languages. async fn returns a Future - a value representing work that hasn’t started yet. Nothing happens until you .await it or give it to an executor (like tokio or async-std).

#[tokio::main]
async fn main() {
    let result = fetch_data().await;
}

The ownership system helps here too. The compiler checks that you don’t hold references across await points in ways that would be unsafe, catching bugs that would be runtime issues in other languages.