Rust, Async Programming
What is Async/Await?
Async/await is a way to write code that can wait for things (like internet requests) without blocking your entire program.
Think of it like waiting in line at a restaurant:
Without Async (Blocking)
fn order_food() {
println!("1. Go to restaurant");
println!("2. Wait in line..."); // ← You can't do anything else here!
println!("3. Order food");
println!("4. Wait for food..."); // ← You can't do anything else here!
println!("5. Get food");
}
With Async (Non-blocking)
async fn order_food() {
println!("1. Go to restaurant");
// While waiting in line, you can check your phone
let line_task = wait_in_line().await; // ← "I'll wait, but don't stop everything"
println!("2. Order food");
// While food cooks, you can read a book
let food_task = cook_food().await; // ← "I'll wait, but don't stop everything"
println!("3. Get food");
}
How Async Works in Rust
1. Async Functions Return "Futures"
// This function returns a "promise" of a String, not a String
async fn fetch_data() -> String {
// Simulate waiting for internet
tokio::time::sleep(Duration::from_secs(2)).await;
"Hello from internet!".to_string()
}
// Usage
async fn main() {
let future = fetch_data(); // ← This doesn't run yet!
println!("Future created, but not executed");
let result = future.await; // ← Now it actually runs and waits
println!("Got result: {}", result);
}
2. The Magic of .await
async fn example() {
println!("Starting...");
// Create multiple futures
let future1 = fetch_data_1(); // ← Just creates a plan
let future2 = fetch_data_2(); // ← Just creates a plan
println!("Both tasks planned!");
// Now actually wait for results
let result1 = future1.await; // ← Actually does the work
let result2 = future2.await; // ← Actually does the work
println!("Both done!");
}
Real Example: Your Bitcoin Wallet
In your Bitcoin wallet, you use async when talking to the internet:
async fn update_balances(&mut self) -> Result<(), Box<dyn StdError>> {
for address_info in &mut self.addresses {
// This could take 1-5 seconds to get response from internet
let response = client.get(&url).send().await?; // ← Wait for internet
if response.status().is_success() {
// This could also take time
let utxos: Vec<UTXO> = response.json().await?; // ← Wait for JSON parsing
address_info.utxos = utxos;
}
}
Ok(())
}
Why Use Async?
1. Better Performance
// Without async - takes 6 seconds total
fn fetch_three_things() {
fetch_thing_1(); // 2 seconds
fetch_thing_2(); // 2 seconds
fetch_thing_3(); // 2 seconds
// Total: 6 seconds
}
// With async - takes 2 seconds total!
async fn fetch_three_things() {
let future1 = fetch_thing_1(); // Start all at once
let future2 = fetch_thing_2(); // Start all at once
let future3 = fetch_thing_3(); // Start all at once
// Wait for all to complete
let (result1, result2, result3) = tokio::join!(future1, future2, future3);
// Total: 2 seconds (all run in parallel)
}
2. Better User Experience
// Without async - UI freezes while loading
fn load_profile() {
let data = fetch_from_internet(); // UI freezes here
update_ui(data);
}
// With async - UI stays responsive
async fn load_profile() {
let data = fetch_from_internet().await; // UI doesn't freeze
update_ui(data);
}
Simple Rules
When to Use await
:
- ✅ Making internet requests (like your Bitcoin API calls)
- ✅ Reading/writing files
- ✅ Database operations
- ✅ Any operation that might take time
When NOT to Use await
:
- ❌ Simple calculations
- ❌ Working with memory data
- ❌ Operations that are always instant
Common Patterns
1. Multiple Async Operations
async fn fetch_user_data(user_id: u32) -> Result<User, Error> {
// Start both requests at the same time
let profile_future = fetch_profile(user_id);
let settings_future = fetch_settings(user_id);
// Wait for both to complete
let (profile, settings) = tokio::join!(profile_future, settings_future);
Ok(User { profile, settings })
}
2. Error Handling
async fn safe_operation() -> Result<String, Error> {
match fetch_data().await {
Ok(data) => Ok(data),
Err(e) => {
println!("Error: {}", e);
Err(e)
}
}
}
3. Timeouts
use tokio::time::{timeout, Duration};
async fn fetch_with_timeout() -> Result<String, Error> {
// Wait max 5 seconds
match timeout(Duration::from_secs(5), fetch_data()).await {
Ok(data) => Ok(data),
Err(_) => Err(Error::Timeout),
}
}
Testing Async Code
#[cfg(test)]
mod tests {
use super::*;
use tokio::test; // ← Special test macro for async
#[test]
async fn test_fetch_data() {
let result = fetch_data().await;
assert_eq!(result, "Hello from internet!");
}
}
Your Bitcoin Wallet Example
Here's how async makes your wallet better:
// Before: Sequential (slow)
async fn update_wallet_slow(&mut self) {
for address in &self.addresses {
let utxos = fetch_utxos(address).await; // Wait for each one
// This takes: 3 addresses × 2 seconds = 6 seconds total
}
}
// After: Parallel (fast)
async fn update_wallet_fast(&mut self) {
let mut futures = Vec::new();
// Start all requests at once
for address in &self.addresses {
let future = fetch_utxos(address);
futures.push(future);
}
// Wait for all to complete
let results = futures::future::join_all(futures).await;
// This takes: 2 seconds total (all run in parallel)
}
Key Takeaways
-
async fn
creates a "plan" (Future), doesn't execute immediately -
.await
actually executes the plan and waits for result - Multiple async operations can run in parallel
- Better performance - don't block while waiting
- Better user experience - UI stays responsive
Simple Mental Model
Think of async/await like cooking multiple dishes:
async fn cook_dinner() {
// Start all dishes at once
let pasta_future = cook_pasta(); // 10 minutes
let sauce_future = cook_sauce(); // 8 minutes
let salad_future = make_salad(); // 5 minutes
// Wait for all to be ready
let (pasta, sauce, salad) = tokio::join!(pasta_future, sauce_future, salad_future);
// Dinner ready in 10 minutes (not 23 minutes!)
}
That's it! Async/await lets you do multiple things at once instead of one after another. Perfect for internet requests, file operations, and anything that takes time.
This guide covered the basics of async/await in Rust. For more advanced topics, check out the Rust async book and Tokio documentation.
Tags: #rust #async #beginners #programming
Top comments (0)