Labs ICT
Pro Login

Async Rust

Async/await for non-blocking operations.

Async Rust

Async Rust lets you write efficient, non-blocking code. It's perfect for I/O-bound work like web servers, file operations, and network requests.

The async/await syntax makes asynchronous code look and feel like synchronous code, while being much more efficient under the hood.

async fn say_hello() {
    println!("Hello!");
}

#[tokio::main]
async fn main() {
    say_hello().await;
}
Try it Yourself ->

Futures and the Future Trait

When you write an async function, it returns a Future. A Future is a value that will eventually produce a result. The Future trait defines how to poll for that result.

use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};

struct MyFuture {
    value: Option<i32>,
}

impl Future for MyFuture {
    type Output = i32;

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        if let Some(value) = self.value.take() {
            Poll::Ready(value)
        } else {
            Poll::Pending
        }
    }
}

#[tokio::main]
async fn main() {
    let future = MyFuture { value: Some(42) };
    let result = future.await;
    println!("Result: {}", result);
}
Try it Yourself ->

The Tokio Runtime

Tokio is the most popular async runtime for Rust. It provides the executor that polls futures and handles the event loop.

use tokio::time::{sleep, Duration};

async fn fetch_data(id: u32) -> String {
    sleep(Duration::from_millis(100)).await;
    format!("Data from source {}", id)
}

#[tokio::main]
async fn main() {
    let data1 = fetch_data(1).await;
    let data2 = fetch_data(2).await;

    println!("{}", data1);
    println!("{}", data2);
}
Try it Yourself ->

.await to Pause Execution

The .await keyword pauses the async function until the future completes. Other tasks can run while this function is waiting, which is what makes async efficient.

use tokio::time::{sleep, Duration};

async fn process_request() -> String {
    println!("Starting request...");
    sleep(Duration::from_millis(200)).await;
    println!("Request completed!");
    "Response data".to_string()
}

#[tokio::main]
async fn main() {
    let result = process_request().await;
    println!("Got: {}", result);
}
Try it Yourself ->

Concurrent Execution

Async really shines when you need to do multiple things at once. Use tokio::join! to run futures concurrently.

use tokio::time::{sleep, Duration};

async fn download_file(name: &str) -> String {
    println!("Starting download: {}", name);
    sleep(Duration::from_millis(100)).await;
    format!("{} downloaded", name)
}

#[tokio::main]
async fn main() {
    let (file1, file2, file3) = tokio::join!(
        download_file("file1.txt"),
        download_file("file2.txt"),
        download_file("file3.txt"),
    );

    println!("{}", file1);
    println!("{}", file2);
    println!("{}", file3);
}
Try it Yourself ->

Why Async Is Important

Async is essential for I/O-bound work. Instead of blocking a thread while waiting for I/O, async lets the thread do other work. This means you can handle thousands of connections with just a few threads.

For CPU-bound work, threads are still the right choice. But for web servers, database connections, and file operations, async is the way to go.