Course contentsShow
← Rust
Lesson 827 of 1,9107. Error Handling PatternsFree lesson

Implementing Basic Custom Errors

Define simple unit-struct errors to classify common failure points.

In this lesson, you will learn how to create your own domain-specific error types using unit structs to make your code's failure points explicit and easy to reason about.

When building an application, you often encounter failures that are specific to your logic—like a UserNotFound error or an InsufficientFunds error. Instead of returning generic strings or complex types, you can define these as simple unit structs. A unit struct is a struct that has no fields, making it incredibly lightweight and perfect for categorizing errors where no extra context is needed at the moment of failure.

Defining Your Errors

To define these errors, you simply declare them using the struct keyword without a body. To make them useful, you should derive the Debug trait, which allows Rust to print them during development or when using unwrap().

#[derive(Debug)]
pub struct InvalidInput;

#[derive(Debug)]
pub struct DatabaseConnectionFailed;

fn process_data(input: &str) -> Result<(), InvalidInput> {
    if input.is_empty() {
        return Err(InvalidInput);
    }
    Ok(())
}

Why use Unit Structs?

  1. Type Safety: Because InvalidInput is its own distinct type, you cannot accidentally pass it where a different error is expected. The compiler will catch these mismatches immediately.
  2. Explicitness: When you see a function return Result<(), DatabaseConnectionFailed>, the intent is crystal clear. It tells any developer (including your future self) exactly what might go wrong without them needing to read the function implementation.
  3. Easy Pattern Matching: You can use match blocks to handle these errors uniquely. Since they are types, you can branch your logic based on the specific error variant, allowing for precise recovery strategies.
match process_data("") {
    Ok(_) => println!("Success!"),
    Err(InvalidInput) => println!("Please provide valid data."),
}

By categorizing your failure points as distinct types, you turn "something went wrong" into "the system knows exactly why this failed," which is a hallmark of robust Rust applications.

Key Takeaway: Use unit structs to define lightweight, unique error types that make your application’s failure modes explicit and type-safe.