How to handle errors in async functions

Handle async errors by returning Result types, using the ? operator for propagation, and matching on JoinHandle results from tokio::spawn.

Handle errors in async functions by returning a Result type and propagating errors with the ? operator or await on the result. Use tokio::spawn to run tasks and handle panics by awaiting the JoinHandle and matching on its Result.

use tokio::task::JoinHandle;

async fn fetch_data() -> Result<String, Box<dyn std::error::Error>> {
    let response = reqwest::get("https://api.example.com")
        .await? // Propagates error if request fails
        .text()
        .await?; // Propagates error if text conversion fails
    Ok(response)
}

#[tokio::main]
async fn main() {
    let handle: JoinHandle<Result<String, Box<dyn std::error::Error>>> = tokio::spawn(fetch_data());
    
    match handle.await {
        Ok(Ok(data)) => println!("Data: {}", data),
        Ok(Err(e)) => eprintln!("Task failed: {}", e),
        Err(e) => eprintln!("Task panicked: {}", e),
    }
}