How to handle database errors in Rust

Handle database errors in Rust by returning Result types and using the ? operator to propagate failures gracefully.

Handle database errors in Rust by returning a Result<T, E> from your functions and propagating errors with the ? operator instead of panicking.

use std::error::Error;
use std::fmt;

#[derive(Debug)]
struct DbError(String);

impl fmt::Display for DbError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Database error: {}", self.0)
    }
}

impl Error for DbError {}

fn query_db() -> Result<String, DbError> {
    // Simulate a database call that might fail
    let data = "user_data";
    Ok(data.to_string())
}

fn main() {
    match query_db() {
        Ok(data) => println!("Got data: {}", data),
        Err(e) => eprintln!("Failed: {}", e),
    }
}

Alternatively, use the ? operator to propagate errors up the call stack:

fn process_data() -> Result<String, DbError> {
    let data = query_db()?; // Propagates error if query_db fails
    Ok(format!("Processed: {}", data))
}

For async database operations, use async fn and return Result<T, E>:

async fn fetch_user(id: u32) -> Result<String, DbError> {
    // Simulate async DB call
    Ok(format!("User {}", id))
}

Use a custom error type or a crate like anyhow or thiserror for production-grade error handling.