← Back to Rust Course | Chapter 10: Error Handling | Lesson 4 of 6

Custom Error Types

Sometimes you design your own kind of error message, so your program can explain exactly what went wrong in its own words.
Syntax
rust
#[derive(Debug)]
enum NameError {
    Variant(Type),
}

impl std::fmt::Display for NameError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "message")
    }
}

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

Defining a Custom Error Enum

A custom error type is typically an enum listing every distinct way an operation can fail, giving callers structured information instead of just a generic string.

Example: Defining a Custom Error Enum

markup
// Auto-generate #[derive(Debug)] for the type below
#[derive(Debug)]
// Define an enum `MathError` listing its possible variants
enum MathError {
    DivideByZero,
    NegativeSquareRoot,
}

// Entry point: execution of the program starts here
fn main() {
    // Declare an immutable variable `err`, set to `MathError::DivideByZero`
    let err = MathError::DivideByZero;
    // Print "{:?}" to the console, substituting in `err` (with a trailing newline)
    println!("{:?}", err);
}

Returning a Custom Error from a Function

Functions can return Result<T, MyError> using the custom error type, letting the compiler enforce that every failure case is represented in the enum.

Example: Returning a Custom Error from a Function

markup
// Auto-generate #[derive(Debug)] for the type below
#[derive(Debug)]
// Define an enum `MathError` listing its possible variants
enum MathError {
    DivideByZero,
}

// Define the function `divide` taking `a`, `b` and returning a `Result<f64, MathError>`
fn divide(a: f64, b: f64) -> Result<f64, MathError> {
    // Check whether `b == 0.0`
    if b == 0.0 {
        Err(MathError::DivideByZero)
    // Otherwise, run this branch
    } else {
        Ok(a / b)
    }
}

// Entry point: execution of the program starts here
fn main() {
    // Print "{:?}" to the console, substituting in `divide(10.0, 0.0)` (with a trailing newline)
    println!("{:?}", divide(10.0, 0.0));
}

Implementing Display for Readable Messages

Implementing std::fmt::Display for a custom error type gives it a clean, human-readable message when printed with {} instead of just the raw Debug output.

Example: Implementing Display for Readable Messages

markup
// Bring `std::fmt` into scope
use std::fmt;

// Auto-generate #[derive(Debug)] for the type below
#[derive(Debug)]
// Define an enum `MathError` listing its possible variants
enum MathError {
    DivideByZero,
}

// Implement methods on `fmt`
impl fmt::Display for MathError {
    // Define the function `fmt` taking `&self`, `f` and returning a `fmt::Result`
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // Match on `self` and branch by pattern
        match self {
            MathError::DivideByZero => write!(f, "cannot divide by zero"),
        }
    }
}

// Entry point: execution of the program starts here
fn main() {
    // Declare an immutable variable `err`, set to `MathError::DivideByZero`
    let err = MathError::DivideByZero;
    // Print "{}" to the console, substituting in `err` (with a trailing newline)
    println!("{}", err);
}

Matching on a Custom Error

Because a custom error is an enum, callers can match on it to respond differently to each specific kind of failure, rather than only having a single generic error message.

Example: Matching on a Custom Error

markup
// Auto-generate #[derive(Debug)] for the type below
#[derive(Debug)]
// Define an enum `MathError` listing its possible variants
enum MathError {
    DivideByZero,
    NegativeSquareRoot,
}

// Define the function `check` taking `n` and returning a `Result<f64, MathError>`
fn check(n: f64) -> Result<f64, MathError> {
    // Check whether `n < 0.0`
    if n < 0.0 {
        Err(MathError::NegativeSquareRoot)
    // Otherwise, run this branch
    } else {
        Ok(n.sqrt())
    }
}

// Entry point: execution of the program starts here
fn main() {
    // Match on `check(-4.0)` and branch by pattern
    match check(-4.0) {
        Ok(v) => println!("Root: {}", v),
        Err(MathError::NegativeSquareRoot) => println!("Cannot take root of negative number"),
        Err(MathError::DivideByZero) => println!("Divide by zero"),
    }
}
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
Common Mistakes
  1. Using String for every error instead of a proper enum, losing the ability for callers to match on specific failure kinds.
  2. Forgetting to implement std::fmt::Display (and often std::error::Error) so the custom error type prints nicely and interoperates with other error-handling code.
  3. Defining a custom error enum but never deriving or implementing Debug, which most error-handling code expects.
Chapter Summary
  • A custom error type is usually an enum with one variant per distinct kind of failure.
  • Implementing std::fmt::Display gives the error type a readable, user-facing message.
  • Implementing std::error::Error lets your custom error interoperate with the wider Rust error-handling ecosystem.
  • Custom error types let callers match on specific failure kinds instead of just a generic message string.
🔒

Chapter Quiz — Complete all 6 topics to unlock

0/6 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.