ProgrammingSystem Programmer

How are asynchronous programming and Future working in Rust? What are the unique features of the async/await implementation and how does it differ from similar mechanisms in other languages?

Pass interviews with Hintsage AI assistant

Answer

Asynchronous programming in Rust is implemented through Future — an object representing a computation whose result will be available in the future. Functions declared with async fn return an anonymous generator struct that implements the Future trait. To run asynchronous code, a runtime (such as Tokio or async-std) is required, as the standard library does not include a built-in event loop.

Key features of Rust:

  • Zero-cost abstractions — the compiler transforms async code into a state machine without heap allocations when possible.
  • No native garbage collector — all resources are managed explicitly, requiring special attention to lifetimes and ownership.
  • Send/Sync — restrictions on the transferability and synchronization of tasks across threads.

Example:

use tokio::time::sleep; use std::time::Duration; async fn foo() { println!("Hello"); sleep(Duration::from_secs(1)).await; println!("World!"); } #[tokio::main] async fn main() { foo().await; }

Trick Question

Can an asynchronous function in Rust be executed immediately upon calling? Why?

Answer: No. Calling async fn returns not a result, but an object of type Future (state machine). To actually execute it, you need to call .await or pass the Future to a runtime. The call only creates a task description but does not execute it.

Example:

async fn answer() -> u32 { 42 } let fut = answer(); // this does not execute, just creates a future let result = fut.await; // execution starts here

Examples of real mistakes due to ignorance of the nuances of the topic


Story

In a high-load backend project, a junior developer declared several async functions but did not call .await anywhere. As a result, the main threads were executing synchronously, leading to performance degradation and a threefold increase in response time.

Story

In a microservice, async API was used, interacting through Tokio. During migration, a developer tried to use async-std and Tokio simultaneously, forgetting that there should be only one event loop. This resulted in hanging and runtime panic because both runtimes conflicted.

Story

One of the team members forgot about the Send/Sync limitations on the types used within Future. When trying to share future across threads, the application crashed with a compilation error requiring Send to be implemented for a certain struct, necessitating a reevaluation of the state storage architecture.