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 {
    async { 42 }
}
🏃

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!

🧠 Test Your Knowledge

What does the 'await' keyword do?