Async / Await in Rust
In this lesson, you will learn about asynchronous programming in Rust
using async and await.
Async programming allows Rust programs to handle multiple tasks efficiently
without blocking threads.
Async Rust is widely used for networking, web servers, APIs, and high-performance systems.
What Is Asynchronous Programming?
Asynchronous programming allows a program to start a task, pause it while waiting for something (like I/O), and continue executing other tasks meanwhile.
This is different from blocking code, where the program waits until a task is fully completed.
Why Async Is Important
Async programming is especially useful when dealing with:
- Network requests
- File operations
- Databases
- Concurrent user requests
Using async helps improve performance and resource utilization.
Async in Rust
Rust provides async support using:
asynckeywordawaitkeyword- Futures (lazy computations)
Async functions return a Future,
which represents a value that will be available later.
Defining an Async Function
An async function is defined using the async keyword.
async fn greet() {
println!("Hello from async function");
}
Calling this function does not execute it immediately. It returns a future instead.
Using await
The await keyword is used to wait for a future to complete.
async fn greet() {
println!("Hello");
}
async fn run() {
greet().await;
}
The .await pauses execution until the async task finishes,
without blocking the thread.
Async Requires a Runtime
Async functions do not run on their own. They need an async runtime to execute.
Common Rust async runtimes include:
- Tokio
- async-std
Tokio is the most widely used runtime in production.
Running Async Code with Tokio
Tokio provides the #[tokio::main] macro
to start an async runtime.
use tokio;
#[tokio::main]
async fn main() {
greet().await;
}
async fn greet() {
println!("Hello from Tokio");
}
Async Tasks
Async tasks are lightweight units of execution. They are cheaper than OS threads.
You can run tasks concurrently using tokio::spawn.
use tokio;
#[tokio::main]
async fn main() {
tokio::spawn(async {
println!("Running in a task");
}).await.unwrap();
}
Async vs Threads
Async and threads solve similar problems but in different ways.
- Threads: OS-managed, heavier
- Async: Runtime-managed, lightweight
Async is ideal for I/O-bound workloads. Threads are better for CPU-bound tasks.
Common Async Use Cases
- Web servers
- REST APIs
- Database queries
- Microservices
- Event-driven systems
📝 Practice Exercises
Exercise 1
Create an async function that prints a message.
Exercise 2
Call an async function using .await.
Exercise 3
Run async code using Tokio.
Exercise 4
Explain the difference between async and threads.
✅ Practice Answers
Answer 1
async fn hello() {
println!("Hello");
}
Answer 2
hello().await;
Answer 3
#[tokio::main]
async fn main() {
hello().await;
}
Answer 4
Async uses non-blocking tasks managed by a runtime, while threads are heavier OS-level executions.
What’s Next?
In the next lesson, you will learn about Futures in Rust, which are the core building blocks behind async and await.