Rust Async/Await
Asynchronous programming in Rust
⚡ What is Rust Async/Await?
Async/await enables non-blocking, concurrent programming in Rust. It allows functions to pause execution while waiting for operations like network requests, letting other tasks run efficiently without blocking threads.
// Async function example
async fn fetch_data() -> String {
// Simulate async operation
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
"Data fetched!".to_string()
}
Async Concepts
Async Functions
Functions that can be paused and resumed
async fn hello() -> String {
"Hello, async world!".to_string()
}
Await Keyword
Wait for async operations to complete
let result = fetch_data().await;
Futures
Represent values that will be available later
use std::future::Future;
fn returns_future() -> impl Future
Runtime
Execute async code (Tokio, async-std)
#[tokio::main]
async fn main() {
println!("Hello, async!");
}
🔹 Basic Async Example
Simple async function with Tokio runtime:
use tokio::time::{sleep, Duration};
async fn say_hello() {
println!("Hello");
sleep(Duration::from_secs(1)).await;
println!("World!");
}
async fn count_to_three() {
for i in 1..=3 {
println!("Count: {}", i);
sleep(Duration::from_millis(500)).await;
}
}
#[tokio::main]
async fn main() {
println!("Starting async operations...");
// Run functions sequentially
say_hello().await;
count_to_three().await;
println!("All done!");
}
Output:
Starting async operations...
Hello
World!
Count: 1
Count: 2
Count: 3
All done!
🔹 Concurrent Execution
Run multiple async operations concurrently:
use tokio::time::{sleep, Duration};
async fn task_one() -> String {
sleep(Duration::from_secs(2)).await;
"Task 1 completed".to_string()
}
async fn task_two() -> String {
sleep(Duration::from_secs(1)).await;
"Task 2 completed".to_string()
}
#[tokio::main]
async fn main() {
println!("Starting concurrent tasks...");
// Run tasks concurrently using join!
let (result1, result2) = tokio::join!(task_one(), task_two());
println!("{}", result1);
println!("{}", result2);
println!("Both tasks finished!");
}
Output:
Starting concurrent tasks...
Task 1 completed
Task 2 completed
Both tasks finished!
🔹 HTTP Client Example
Making async HTTP requests:
🔸 Cargo.toml dependencies
[dependencies]
tokio = { version = "1.0", features = ["full"] }
reqwest = { version = "0.11", features = ["json"] }
🔸 Async HTTP client
use reqwest;
use serde::Deserialize;
#[derive(Deserialize, Debug)]
struct Post {
id: u32,
title: String,
body: String,
}
async fn fetch_post(id: u32) -> Result {
let url = format!("https://jsonplaceholder.typicode.com/posts/{}", id);
let post: Post = reqwest::get(&url).await?.json().await?;
Ok(post)
}
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
println!("Fetching post...");
match fetch_post(1).await {
Ok(post) => {
println!("Post ID: {}", post.id);
println!("Title: {}", post.title);
}
Err(e) => println!("Error: {}", e),
}
Ok(())
}
🔹 Error Handling in Async
Handle errors in async functions:
use tokio::time::{sleep, Duration};
async fn might_fail(should_fail: bool) -> Result {
sleep(Duration::from_secs(1)).await;
if should_fail {
Err("Something went wrong!")
} else {
Ok("Success!".to_string())
}
}
async fn handle_multiple_operations() {
// Using ? operator in async functions
let result1 = might_fail(false).await;
let result2 = might_fail(true).await;
match result1 {
Ok(msg) => println!("Operation 1: {}", msg),
Err(e) => println!("Operation 1 failed: {}", e),
}
match result2 {
Ok(msg) => println!("Operation 2: {}", msg),
Err(e) => println!("Operation 2 failed: {}", e),
}
}
#[tokio::main]
async fn main() {
handle_multiple_operations().await;
}
Output:
Operation 1: Success!
Operation 2 failed: Something went wrong!
🔹 Spawning Tasks
Run tasks in the background:
use tokio::time::{sleep, Duration};
async fn background_task(name: &str, duration: u64) {
for i in 1..=3 {
println!("{} - Step {}", name, i);
sleep(Duration::from_millis(duration)).await;
}
println!("{} completed!", name);
}
#[tokio::main]
async fn main() {
println!("Starting background tasks...");
// Spawn tasks to run concurrently
let task1 = tokio::spawn(background_task("Task A", 300));
let task2 = tokio::spawn(background_task("Task B", 500));
// Do other work while tasks run
sleep(Duration::from_millis(100)).await;
println!("Main thread doing other work...");
// Wait for all tasks to complete
let _ = tokio::join!(task1, task2);
println!("All background tasks finished!");
}
Output:
Starting background tasks...
Task A - Step 1
Task B - Step 1
Main thread doing other work...
Task A - Step 2
Task B - Step 2
Task A - Step 3
Task A completed!
Task B - Step 3
Task B completed!
All background tasks finished!