Rust Lesson 27 – Error Handling | Dataplexa

Error Handling in Rust

In this lesson, you will learn how error handling works in Rust. Rust takes a unique and powerful approach to errors, helping developers write safe and reliable programs.

Unlike many languages that rely heavily on exceptions, Rust encourages handling errors explicitly at compile time.


Why Error Handling Matters

Errors are unavoidable in real-world programs. Files may not exist, network requests can fail, and user input may be invalid.

Rust’s error handling system ensures that these situations are handled safely and predictably.


Two Types of Errors in Rust

Rust categorizes errors into two main types:

  • Recoverable errors – errors that can be handled gracefully
  • Unrecoverable errors – serious errors that stop execution

Unrecoverable Errors with panic!

The panic! macro is used when something goes seriously wrong. It stops the program immediately.

fn main() {
    panic!("Something went wrong!");
}

When a panic occurs, Rust prints an error message and terminates the program.


Recoverable Errors with Result

Most errors in Rust are handled using the Result enum.

The Result type has two variants:

  • Ok(value) – represents success
  • Err(error) – represents failure

Basic Result Example

Here is an example of a function that may fail:

fn divide(a: i32, b: i32) -> Result {
    if b == 0 {
        Err(String::from("Cannot divide by zero"))
    } else {
        Ok(a / b)
    }
}

Handling Result with match

You can use a match statement to handle both success and failure cases.

match divide(10, 2) {
    Ok(value) => println!("Result: {}", value),
    Err(err) => println!("Error: {}", err),
}

Using unwrap()

The unwrap() method extracts the value from Ok or panics if an error occurs.

This should only be used when you are sure the result is valid.

let result = divide(10, 2).unwrap();

Using expect()

The expect() method works like unwrap() but allows you to provide a custom error message.

let result = divide(10, 0).expect("Division failed");

Propagating Errors with ?

The ? operator simplifies error propagation. It returns the error to the caller automatically.

fn safe_divide(a: i32, b: i32) -> Result {
    let result = divide(a, b)?;
    Ok(result)
}

Custom Error Types

For larger programs, you may want to define your own error types.

enum MathError {
    DivisionByZero,
}

Why Rust’s Error Handling Is Powerful

Rust forces developers to think about errors at compile time, which leads to more robust and predictable programs.

  • No hidden exceptions
  • Explicit error handling
  • Safer production code

📝 Practice Exercises


Exercise 1

Write a function that returns a Result when dividing two numbers.

Exercise 2

Handle a Result using a match expression.

Exercise 3

Use the ? operator in a function.

Exercise 4

Create a custom error enum.


✅ Practice Answers


Answer 1

fn divide_safe(a: i32, b: i32) -> Result {
    if b == 0 {
        Err(String::from("Zero division"))
    } else {
        Ok(a / b)
    }
}

Answer 2

match divide_safe(5, 0) {
    Ok(v) => println!("{}", v),
    Err(e) => println!("{}", e),
}

Answer 3

fn wrapper(a: i32, b: i32) -> Result {
    let value = divide_safe(a, b)?;
    Ok(value)
}

Answer 4

enum AppError {
    InvalidInput,
}

What’s Next?

In the next lesson, you will learn about Iterators in Rust — a powerful way to process collections efficiently.