Advanced

Error Handling

Handle failures with Result and Option.

What you will see

not found

About 2 min of reading

Quick check

Before this new lesson, one small question about the lesson you just finished. Recall it, do not look it up.

Try It

You will return Result values, use the ? operator to propagate errors, and match on Option.

1

I write a function that returns a Result. For a non-zero divisor it returns Ok; the match prints the value.

fn divide(a: f64, b: f64) -> Result { if b == 0.0 { Err("divide by zero".into()) } else { Ok(a / b) } } fn main() { match divide(10.0, 2.0) { Ok(v) => println!("{}", v), Err(e) => println!("error: {}", e), } }

Before you run it, say out loud what it will print. Then open the answer.

What it prints
5
2

I divide by zero. The function returns Err and the match prints the error message.

fn divide(a: f64, b: f64) -> Result { if b == 0.0 { Err("divide by zero".into()) } else { Ok(a / b) } } fn main() { match divide(10.0, 0.0) { Ok(v) => println!("{}", v), Err(e) => println!("error: {}", e), } }

Before you run it, say out loud what it will print. Then open the answer.

What it prints
error: divide by zero
3

I use the ? operator to propagate a parse error. The function returns Result so ? can return early on failure.

fn parse_number(s: &str) -> Result { let n = s.parse::().map_err(|e| e.to_string())?; Ok(n) } fn main() { match parse_number("42") { Ok(n) => println!("{}", n), Err(e) => println!("error: {}", e), } }

Before you run it, say out loud what it will print. Then open the answer.

What it prints
42
4

I search a string and match on the Option result. The Some arm prints the index when the substring is found.

fn find(s: &str, needle: &str) -> Option { s.find(needle) } fn main() { match find("hello", "ll") { Some(i) => println!("found at {}", i), None => println!("not found"), } }

Before you run it, say out loud what it will print. Then open the answer.

What it prints
found at 2
5

I search for a substring that is not present. The match falls through to the None arm.

fn find(s: &str, needle: &str) -> Option { s.find(needle) } fn main() { match find("hello", "zz") { Some(i) => println!("found at {}", i), None => println!("not found"), } }

Before you run it, say out loud what it will print. Then open the answer.

What it prints
not found

Data Flow

A function returns Result or Option; callers unwrap or propagate.
1 Operation produces Ok or Err
↓
2 Caller matches on the result
↓
3 Ok unwraps the value
↓
4 Err propagates with ?
Why this works
Result and Option

Two enums handle absence:

enum Result<T, E> { Ok(T), Err(E) }
enum Option<T> { Some(T), None }

Result is for operations that can fail. Option is for values that may be missing. Both are first-class types you match on.

Error propagation

Chain fallible operations:

fn run() -> Result<(), Box<dyn Error>> {
    let n = "42".parse::<i32>()?;
    let double = n * 2;
    Ok(double)
}

Box<dyn Error> is a boxed trait object that can hold any error type. ? propagates errors up the call chain.

Examples

The Result type

Returning errors

fn divide(a: f64, b: f64) -> Result<f64, String> {
    if b == 0.0 {
        Err("divide by zero".into())
    } else {
        Ok(a / b)
    }
}

Result<T, E> returns either Ok(value) or Err(error). Callers match on it to handle both cases.

Why it works: errors are explicit values, not thrown exceptions.

The ? operator

Propagating errors

fn parse_number(s: &str) -> Result<i32, String> {
    let n = s.parse::<i32>()?;   // propagates error
    Ok(n)
}

? returns the error early if the value is Err. On Ok, it unwraps the value. It keeps error-handling code short.

Why it works: ? only works in functions that return Result or Option.

Option type

Optional values

fn find(s: &str, needle: &str) -> Option<usize> {
    s.find(needle)
}

match find("hello", "ll") {
    Some(i) => println!("found at {i}"),
    None => println!("not found"),
}

Option<T> is either Some(value) or None. It replaces null pointers and forces you to handle the missing case.

Why it works: you cannot accidentally dereference a null.

Words this lesson uses

Each one is defined here on this site. Open the entry for the full definition and the other words it points at.

← Advanced Ownership
Previous Lesson
Advanced Ownership
Next Lesson
Modules and Packages
Modules and Packages →