Error Handling

The Ok and Err Variants

Ok(T) and Err(E) are the two variants of the standard library's Result<T, E> enum. Because both variants are automatically imported into every file by Rust's prelude, you write Ok(value) and Err(error) directly without the Result:: prefix.

Symmetry and Type Roles of Ok(T) and Err(E)

  • Ok(T): Wraps the success value of type T.
  • Err(E): Wraps the failure diagnostic value of type E (which can be any type—an error enum, a String, &'static str, or an integer code).

Every Ok(v) or Err(e) expression constructs a full Result<T, E> value. If a single expression (such as let r = Ok(10);) only mentions Ok(10) without constraining E (or only mentions Err("bad") without constraining T), the compiler requires a type annotation so both T and E are known (E0282):

fn main() {
    // Both `T` (`u32`) and `E` (`&str`) are specified in the annotation:
    let success: Result<u32, &str> = Ok(200);
    let failure: Result<u32, &str> = Err("connection refused");

    println!("success = {success:?}, failure = {failure:?}");
}
success = Ok(200), failure = Err("connection refused")

Extracting and Transforming Ok and Err

Goal Idiomatic Method / Syntax Behavior
Pattern match both cases match res { Ok(v) => ..., Err(e) => ... } Exhaustively handles both Ok and Err
Unwrap Ok(v) or return early let Ok(v) = res else { return ...; }; or res? Extracts v into scope or exits on Err
Transform Ok(v) while leaving Err(e) untouched res.map(|v| v * 2) Applies closure only to Ok(v)
Transform Err(e) while leaving Ok(v) untouched res.map_err(|e| format!("failed: {e}")) Applies closure only to Err(e)
Borrow inner payload without moving res res.as_ref() / res.as_mut() Converts &Result<T, E> into Result<&T, &E>
fn parse_and_scale(raw: &str, factor: i32) -> Result<i32, String> {
    raw.trim()
        .parse::<i32>()
        .map(|n| n * factor) // Runs ONLY if `.parse()` returned `Ok(n)`
        .map_err(|e| format!("could not parse '{raw}': {e}")) // Runs ONLY if `.parse()` returned `Err(e)`
}

fn main() {
    println!("{:?}", parse_and_scale(" 15 ", 3));
    println!("{:?}", parse_and_scale("xyz", 3));
}
Ok(45)
Err("could not parse 'xyz': invalid digit found in string")

Passing Ok as a Constructor Function to .map(Ok)

Because Ok and Err are 1-element tuple variants, Ok is also a first-class function fn(T) -> Result<T, E> (and Err is fn(E) -> Result<T, E>). You can pass Ok directly to Option::map or Iterator::map:

fn main() {
    let numbers = [1, 2, 3];
    let wrapped: [Result<i32, &str>; 3] = numbers.map(Ok);
    println!("Wrapped in Ok: {wrapped:?}");
}
Wrapped in Ok: [Ok(1), Ok(2), Ok(3)]