Rust Tutorial · Error Handling · Lesson 19 of 29

Rust panic! and Custom Error Types

When to panic and when to return errors, and how to define error types that callers can match on.

Create a free account to save this page, take notes and track your progress. Sign in

panic! is for bugs

panic! (and unwrap()/expect() on failure) stops the thread: use it for situations that mean the program itself is wrong. For expected failures (bad input, missing files, network errors), return Result.

Custom error enums

An enum lists the ways an operation can fail; implementing Display and std::error::Error makes it work with ?, Box<dyn Error> and error-reporting libraries:

A custom error enum
use std::fmt;

#[derive(Debug)]
enum WithdrawError {
    InsufficientFunds { needed: u64, available: u64 },
    AccountFrozen,
}

impl fmt::Display for WithdrawError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Self::InsufficientFunds { needed, available } =>
                write!(f, "needed {needed}, only {available} available"),
            Self::AccountFrozen => write!(f, "account is frozen"),
        }
    }
}

impl std::error::Error for WithdrawError {}

fn withdraw(balance: u64, amount: u64, frozen: bool) -> Result<u64, WithdrawError> {
    if frozen { return Err(WithdrawError::AccountFrozen); }
    balance.checked_sub(amount).ok_or(WithdrawError::InsufficientFunds { needed: amount, available: balance })
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    println!("left: {}", withdraw(100, 30, false)?);
    if let Err(e) = withdraw(100, 300, false) { println!("error: {e}"); }
    match withdraw(100, 10, true) {
        Err(WithdrawError::AccountFrozen) => println!("call support"),
        other => println!("{other:?}"),
    }
    Ok(())
}

Output

left: 70
error: needed 300, only 100 available
call support

Run this on your computer — see “Get Started” for setup.

In applications, the anyhow crate simplifies error handling; in libraries, thiserror derives the boilerplate above.

Test yourself

When should Rust code return Result instead of panicking?

  1. Never
  2. When failure is expected, like bad input or a missing file
  3. Only in libraries
  4. Only in async code
Show answer

Answer: When failure is expected, like bad input or a missing file

Was this page helpful?