Asynchronous Programming in Rust: A Complete Guide for Beginners

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction to Asynchronous Programming in Rust



Asynchronous programming is an approach that allows your application to efficiently perform multiple tasks simultaneously without blocking the main execution thread. In Rust, this approach is implemented through the async/await model, which provides the memory safety and high performance characteristic of the language.



Unlike traditional multithreading, asynchronous code runs within one or several threads, switching between tasks during waiting periods (for example, I/O or network requests). This makes it ideal for creating high-load web servers, network applications, and data processing tools.



In this article, we will break down the basics of asynchronous programming in Rust: from key concepts to practical examples. You will learn how Future, async, and await work, get acquainted with the popular runtime Tokio, and write your first asynchronous project.



Core Concepts: Future, async, and await



What is a Future?



In Rust, an asynchronous task is represented by the Future trait. A Future is a value that may not be ready yet but will be available in the future. Each Future has a poll method that checks whether the task is complete. If the task is still running, it returns Pending; if it is complete, it returns Ready(value).



By itself, a Future does not perform any work. It only describes what should happen. To execute asynchronous code, you need a runtime — an executor that polls Futures and manages their execution.



The async and await Keywords



The async keyword transforms a regular function into an asynchronous one that returns a Future. For example:



async fn hello() -> String {    "Hello, world!".to_string()}


To get the result from a Future, you use .await. It suspends the execution of the current function until the Future is complete:



let result = hello().await;println!("{}", result);


Important: .await can only be used inside an asynchronous function or an async {} block.



Lifecycle of an Asynchronous Task



  • Creation of a Future (calling an async function).
  • Passing the Future to the runtime for execution.
  • The runtime polls the Future via poll.
  • If the task blocks (waiting for I/O), the runtime switches to another task.
  • When the result is ready, the Future returns Ready.


The Ecosystem: Tokio and async-std



To work with asynchronous code in Rust, asynchronous runtimes are used. The two most popular ones are Tokio and async-std.



Tokio



Tokio is the industry standard for asynchronous programming in Rust. It provides a multi-threaded scheduler, asynchronous I/O, timers, synchronization, and network protocols. Most major projects (for example, Hyper, Tonic, Axum) are built on Tokio.



To get started with Tokio, add the dependency to Cargo.toml:



[dependencies]tokio = { version = "1", features = ["full"] }


The main macro for running asynchronous code is #[tokio::main]:



#[tokio::main]async fn main() {    println!("Hello from Tokio!");}


async-std



async-std is an alternative runtime that strives to be as similar as possible to the Rust standard library. It is simpler for beginners but less popular in production.



Example

Blogs

Book Recommendations