← Back to Rust series
🦀
Error handling
Error handling · Prerequisite: types 1-4

15. Working with Result<T,E> and Option<T>

Rust's error handling boils down to two enums — `Option<T>` for "may not exist" and `Result<T,E>` for "may fail." There's no try/catch; failure is encoded into types, so the compiler catches forgotten handling. This lesson covers conversions and helper methods between the two.

RustResultOptionerror handlingmatch
Duration
~1.5 hours
Level
📊 Intermediate
Prerequisite
🎯 Types 1-4
OUTCOME
Rust's error handling boils down to two enums — `Option<T>` for "may not exist" and `Result<T,E>` for "may fail." There's no try/catch; failure is encoded into types, so the compiler catches forgotten handling. This lesson covers conversions and helper methods between the two.

What you'll learn

  • 1Tell apart Result<T,E> and Option<T>
  • 2Branch with match or helper methods
  • 3Convert with `.ok()` / `.ok_or()`
  • 4Mark fallible functions in their signature
  • 5Know when to panic vs. return Result

Overview

Other languages hide failure either invisibly (unchecked exceptions) or with a separate throws clause. Rust puts it on the return type — the caller sees how a function can fail at a glance, and missing the handling is a compile error.

Core Concepts

1) Result<T, E>

rust
enum Result<T, E> {
    Ok(T),
    Err(E),
}

Carries both success type T and failure type E in the signature; both can hold data.

2) Option<T> vs Result<T,E>

AspectOptionResult
Purposemay / may-not existsuccess / failure with detail
AbsenceNone (no info)Err(E) (with reason)
When to uselist lookup, optional fieldI/O, network, parsing

3) Common helpers

MethodBehavior
.unwrap()Ok/Some → inner, else panic
.expect("msg")panic with message
.unwrap_or(d)default on failure
.unwrap_or_else(|e| ...)computed default
.ok()Result → Option (discards Err)
.map(|x| ...)transform on Ok/Some
.map_err(|e| ...)transform Err only

Hands-on Examples

Parsing with a Result return:

rust
fn parse_age(s: &str) -> Result<u32, String> {
    match s.trim().parse::<u32>() {
        Ok(n) if n > 150 => Err(format!("unrealistic age: {}", n)),
        Ok(n)            => Ok(n),
        Err(e)           => Err(format!("parse failed: {}", e)),
    }
}

fn main() {
    for s in ["30", "abc", "999"] {
        match parse_age(s) {
            Ok(n)  => println!("{} OK -> {}", s, n),
            Err(e) => println!("{} ERR -> {}", s, e),
        }
    }
}

Option ↔ Result:

rust
fn find(v: &[i32], target: i32) -> Option<usize> {
    v.iter().position(|&x| x == target)
}

fn find_or_err(v: &[i32], target: i32) -> Result<usize, String> {
    find(v, target).ok_or(format!("{} not found", target))
}

fn main() {
    let v = [10, 20, 30];
    println!("{:?}", find_or_err(&v, 20));  // Ok(1)
    println!("{:?}", find_or_err(&v, 99));  // Err("99 not found")
}

Helper chaining:

rust
fn main() {
    let s = "  42  ";
    let n = s.trim().parse::<i32>()
        .map(|x| x * 2)
        .unwrap_or(-1);
    println!("{}", n); // 84
}

Common Mistakes

Q. Do I really write match every time?

A. No — the next lesson's `?` operator reduces "fail-and-return" to one character. `.map` / `.and_then` chains cover most of the rest.

Q. Can I just .unwrap()?

A. OK for prototypes / examples / tests. In production code only when **"this can never be None/Err" is structurally obvious**. Never on user input or I/O results.

Q. Option or Result?

A. **If the reason for failure is meaningful, Result. If it's pure absence, Option.** Example: list lookup → Option, file open → Result.

Recap

  • Option = absence; Result = failure with reason
  • Fallible functions spell out their error type → forgotten handling is a compile error
  • Branch with match, .unwrap_or, .map, .ok_or, etc.
  • Use .unwrap() carefully

Try It Yourself

  1. Write `fn divide(a: i32, b: i32) -> Result<i32, String>` (Err when b=0)
  2. Parse a user-entered string as u32, accept only 1~120 (Err otherwise)
  3. Collect Ok values from `Vec<Result<i32, String>>` and sum them
Example code / lecture materials

All lecture materials and example code are openly available on GitHub.

View on GitHub ↗