Error Handling

unwrap() and expect()

unwrap() and expect() are methods defined on both Option<T> and Result<T, E> that consume the wrapper and return the inner value T if present (Some(T) or Ok(T)), or immediately panic if the value is None or Err(E).

How unwrap() and expect() Work

Option<T> / Result<T, E>
  ├── Some(v) / Ok(v)  ──> unwrap() or expect(msg) ──> returns v
  └── None    / Err(e) ──> unwrap() or expect(msg) ──> panics!
Method Receiver Type On Some(v) / Ok(v) On None / Err(e)
opt.unwrap() Option<T> Returns v Panics with "called Option::unwrap()on aNone value"
opt.expect(msg) Option<T> Returns v Panics with "{msg}"
res.unwrap() Result<T, E> (E: Debug) Returns v Panics with "called Result::unwrap()on anErr value: {e:?}"
res.expect(msg) Result<T, E> (E: Debug) Returns v Panics with "{msg}: {e:?}"

On Result<T, E>, both unwrap() and expect() require E: std::fmt::Debug so that the panic message can format the error value E.

Extracting Values When Success Is Present

fn main() {
    let host: Option<&str> = Some("127.0.0.1");
    let port: Result<u16, _> = "8080".parse();

    let h = host.unwrap();
    let p = port.expect("hardcoded port literal should be valid u16");

    println!("Binding to {h}:{p}");
}
Binding to 127.0.0.1:8080

Panic Behavior on None and Err(e)

Calling unwrap() on Err(e) produces a generic panic message that shows the file and line number alongside the debug representation of e:

fn main() {
    let count: i32 = "not_a_number".parse().unwrap();
    println!("{count}");
}
thread 'main' panicked at src/main.rs:2:44:
called `Result::unwrap()` on an `Err` value: ParseIntError { kind: InvalidDigit }

Calling expect() attaches context explaining why the value was assumed to be valid, making crash diagnostics clearer:

fn main() {
    let count: i32 = "not_a_number"
        .parse()
        .expect("WORKER_THREADS must be a valid integer");
    println!("{count}");
}
thread 'main' panicked at src/main.rs:4:10:
WORKER_THREADS must be a valid integer: ParseIntError { kind: InvalidDigit }

unwrap_err() and expect_err() for Testing Failures

When testing that an operation fails, Result<T, E> provides the inverse methods unwrap_err() and expect_err(msg) (requiring T: Debug), which extract the error E from Err(e) or panic if the value is Ok(v):

fn main() {
    let err = "99999".parse::<u8>().expect_err("99999 overflows u8");
    println!("Caught expected parse error: {err}");
}
Caught expected parse error: number too large to fit in target type

Choosing Between expect(), unwrap(), and Non-Panicking Alternatives

Scenario Recommended Approach
Recoverable runtime failure (user input, network, files) Propagate with ? or handle with match / let ... else
Missing value has a sensible fallback Use unwrap_or(default) or unwrap_or_else(f)
Compile-time constant or invariant proven by surrounding code Use expect("reason invariant holds") to document the proof
Unit tests and quick throwaway prototypes unwrap() or expect() is concise and fails the test immediately