Error Handling in Rust: Result, the ? Operator, thiserror and anyhow

A
Author
28 Aug, 2026 1 min read 1.09K views

Rust has no exceptions. Functions that can fail return Result<T, E>, and the compiler makes sure you deal with the error case.

Result and match

use std::fs;

match fs::read_to_string("config.toml") {
    Ok(text) => println!("{} bytes", text.len()),
    Err(e) => eprintln!("could not read config: {e}"),
}

The ? operator

? returns the error to the caller if there is one, otherwise unwraps the value. It keeps the happy path readable:

use std::{fs, io};

fn read_port() -> Result<u16, Box<dyn std::error::Error>> {
    let text = fs::read_to_string("port.txt")?;   // io::Error
    let port = text.trim().parse::<u16>()?;        // ParseIntError
    Ok(port)
}

Libraries: precise errors with thiserror

use thiserror::Error;

#[derive(Debug, Error)]
pub enum PaymentError {
    #[error("card declined: {0}")]
    Declined(String),
    #[error("amount must be positive, got {0}")]
    InvalidAmount(i64),
    #[error("gateway unavailable")]
    Gateway(#[from] reqwest::Error),   // ? converts reqwest errors automatically
}

pub async fn charge(amount: i64) -> Result<String, PaymentError> {
    if amount <= 0 {
        return Err(PaymentError::InvalidAmount(amount));
    }
    let id = gateway::charge(amount).await?;
    Ok(id)
}

Callers can match on each variant and react differently.

Applications: easy context with anyhow

use anyhow::{Context, Result};

fn main() -> Result<()> {
    let config = std::fs::read_to_string("config.toml")
        .context("failed to read config.toml")?;
    let port: u16 = config.trim().parse().context("port must be a number")?;
    println!("listening on {port}");
    Ok(())
}

Guidelines

  • Libraries: thiserror enums so users can handle specific cases.
  • Binaries and top-level code: anyhow with .context() for helpful messages.
  • Use unwrap() and expect() only for genuine bugs (or in tests), never for expected failures like bad user input.
  • Option<T> works with ? too, inside functions that return Option.
Was this article helpful?

Comments

No comments yet. Have a question or a tip? Be the first to share it.

Leave a comment

Comments are reviewed before they appear.