Async
🤔 refer to The State of Async Rust
The State
graph LR; A("Async madness") A-.Try to cover Rust standard.->C("async-std<br/>abandoned 😱") A-.Try to cover any runtime.->B("Tokio<br/>Multi threaded by default = complex 🫠")
Async vs Threads
Threads
use std::error::Error; use std::fs::File; use std::io::Read; use std::path::Path; use std::{thread, time}; // This fn could be used by both async and sync callers, // eliminating the need for an asynchronous runtime. fn read_contents<T: AsRef<Path>>(file: T) -> Result<String, Box<dyn Error>> { let mut file = File::open(file)?; let mut contents = String::new(); file.read_to_string(&mut contents)?; return Ok(contents); } fn main() { thread::scope(|scope| { // worker thread 1 scope.spawn(|| { let contents = read_contents("foo.txt"); // do something with contents }); // worker thread 2 scope.spawn(|| { let contents = read_contents("bar.txt"); // ... }); // worker thread 3 scope.spawn(|| { let contents = read_contents("baz.txt"); // ... }); }); // Threads get joined automatically }
Async
#[tokio::main] async fn main() { // This will print a warning, but compile and do nothing at runtime read_contents("foo.txt"); // futures do nothing unless you `.await` or poll them }
🤔 refer to Asynchronous Programming in Rust
If you don't need async for performance reasons, threads can often be the simpler alternative.
Best practices
synchronous
by default.- opt into
async
as needed. - less main with
#[tokio::main]
.
#[get("/")] fn index() -> impl Response { let users = db::get_users(); Response::ok().body(render_template("index.html", users)) } // A route, which profits from concurrent IO // It sends multiple requests to an external API and aggregates the results // Note how the function itself is does not need to be async #[get("/users")] fn users(count: usize) -> impl Response { // Start a local, single-threaded runtime with smol's async-executor let rt = smol::LocalExecutor::new(); // Run the async code on the runtime let results = rt.run(async { let mut results = Vec::new(); for id in 0..count { let result = reqwest::get(format!("https://api.example.com/users/{}", id)).await?; results.push(result); } Ok(results) }); Response::ok().body(render_template("users.html", results)) } // This does not need to be async either! // In the background, it might use a thread pool to handle multiple requests fn main() -> Result<()> { let app = App::new() .mount("/", index) .mount("/users", users) .run(); }
Read more 👉 Asynchronous Programming in Rust