Error Handling

map() and map_err()

map() and map_err() are adapter methods that transform the value inside an Option<T> or Result<T, E> by applying a closure to the targeted variant while passing the other variant through untouched.

How map() and map_err() Transform Variants

Option::map(f)
  ├── Some(v) ──> Some(f(v))
  └── None    ──> None

Result::map(f)
  ├── Ok(v)   ──> Ok(f(v))
  └── Err(e)  ──> Err(e)

Result::map_err(g)
  ├── Ok(v)   ──> Ok(v)
  └── Err(e)  ──> Err(g(e))
Method Receiver Signature Transforms Leaves Untouched
opt.map(f) Option<T> FnOnce(T) -> U Some(T) -> Some(U) None
res.map(f) Result<T, E> FnOnce(T) -> U Ok(T) -> Ok(U) Err(E)
res.map_err(g) Result<T, E> FnOnce(E) -> F Err(E) -> Err(F) Ok(T)
opt.map_or(default, f) Option<T> U, FnOnce(T) -> U Some(T) -> U (unwrapped) None -> default
opt.map_or_else(d, f) Option<T> FnOnce() -> U, FnOnce(T) -> U Some(T) -> f(v) None -> d()

Transforming Some(T) and Ok(T) with map()

map() consumes the original Option<T> or Result<T, E> and applies the closure only when a value is present:

fn main() {
    let raw_name: Option<&str> = Some("  ada lovelace  ");
    let missing_name: Option<&str> = None;

    let normalized = raw_name.map(str::trim).map(str::to_uppercase);
    let missing_norm = missing_name.map(str::trim).map(str::to_uppercase);

    println!("normalized:   {normalized:?}");
    println!("missing_norm: {missing_norm:?}");

    let parsed: Result<i32, _> = "21".parse::<i32>().map(|n| n * 2);
    let failed: Result<i32, _> = "xx".parse::<i32>().map(|n| n * 2);

    println!("parsed: {parsed:?}");
    println!("failed: {failed:?}");
}
normalized:   Some("ADA LOVELACE")
missing_norm: None
parsed: Ok(42)
failed: Err(ParseIntError { kind: InvalidDigit })

Transforming Err(E) with map_err()

map_err() applies a closure only to the Err(E) variant of a Result<T, E>, converting Result<T, E> into Result<T, F>:

#[derive(Debug)]
struct CliError(String);

fn parse_threads(arg: &str) -> Result<usize, CliError> {
    arg.parse::<usize>()
        .map(|n| n.max(1))
        .map_err(|e| CliError(format!("invalid --threads value {arg:?}: {e}")))
}

fn main() {
    println!("0 threads  -> {:?}", parse_threads("0"));
    println!("8 threads  -> {:?}", parse_threads("8"));
    println!("bad input  -> {:?}", parse_threads("four"));
}
0 threads  -> Ok(1)
8 threads  -> Ok(8)
bad input  -> Err(CliError("invalid --threads value \"four\": invalid digit found in string"))

Folding Into a Single Value with map_or() and map_or_else()

When you want to transform the inner value and extract it in a single step—supplying a fallback if the receiver is None or Err(e)—use map_or(default, f) (for a constant fallback) or map_or_else(fallback_fn, f) (for a lazily computed fallback):

fn main() {
    let tag: Option<&str> = Some("v1.4.0");
    let no_tag: Option<&str> = None;

    let len_a = tag.map_or(0, str::len);
    let len_b = no_tag.map_or(0, str::len);
    println!("len_a = {len_a}, len_b = {len_b}");

    let ok_res: Result<i32, &str> = Ok(10);
    let err_res: Result<i32, &str> = Err("timeout");

    let msg_ok = ok_res.map_or_else(|e| format!("ERR: {e}"), |v| format!("OK: {}", v * 10));
    let msg_err = err_res.map_or_else(|e| format!("ERR: {e}"), |v| format!("OK: {}", v * 10));
    println!("{msg_ok} | {msg_err}");
}
len_a = 6, len_b = 0
OK: 100 | ERR: timeout