Option<T> and Result<T, E> in Depth

Result<T, E> Combinators

Result<T, E> provides a symmetric suite of combinators that transform either the success track (Ok(T)) or the failure track (Err(E)), chain fallible steps, or recover from errors without verbose match expressions.

Overview of Result<T, E> Combinators

Success Track (`Ok(T)`):   ── .map(f) ──────> Ok(f(T))
                           ── .and_then(g) ─> g(T) -> Result<U, E>
                           │
Failure Track (`Err(E)`):  ── .map_err(h) ──> Err(h(E))
                           ── .or_else(r) ──> r(E) -> Result<T, F>
Combinator Acts On Leaves Unchanged Closure / Argument Signature Return Type
res.map(f) Ok(T) Err(E) FnOnce(T) -> U Result<U, E>
res.map_err(f) Err(E) Ok(T) FnOnce(E) -> F Result<T, F>
res.and_then(f) Ok(T) Err(E) FnOnce(T) -> Result<U, E> Result<U, E>
res.or_else(f) Err(E) Ok(T) FnOnce(E) -> Result<T, F> Result<T, F>
res.and(res_b) Ok(T) Err(E) Result<U, E> Result<U, E>
res.or(res_b) Err(E) Ok(T) Result<T, F> Result<T, F>
res.unwrap_or_else(f) Err(E) Unwraps Ok(T) FnOnce(E) -> T T

Chaining .and_then() and Recovering with .or_else()

  • .and_then(f) runs the next fallible step f(val) only if the current result is Ok(val).
  • .or_else(f) attempts a fallback recovery operation f(err) only if the current result is Err(err):
fn parse_port(raw: &str) -> Result<u16, String> {
    raw.trim()
        .parse::<u16>()
        .map_err(|e| format!("parse error: {e}"))
        .and_then(|port| {
            if port >= 1024 {
                Ok(port)
            } else {
                Err(format!("port {port} is privileged (< 1024)"))
            }
        })
}

fn resolve_port(primary: &str, fallback: &str) -> Result<u16, String> {
    // If `primary` fails, `.or_else()` attempts `fallback`:
    parse_port(primary).or_else(|_primary_err| parse_port(fallback))
}

fn main() {
    println!("Primary valid:   {:?}", resolve_port("8080", "9000"));
    println!("Fallback used:   {:?}", resolve_port("80", "9000"));
    println!("Both invalid:    {:?}", resolve_port("bad", "22"));
}
Primary valid:   Ok(8080)
Fallback used:   Ok(9000)
Both invalid:    Err("port 22 is privileged (< 1024)")

Querying Result Predicates (is_ok_and and is_err_and)

In addition to .is_ok() and .is_err(), res.is_ok_and(f) and res.is_err_and(f) test both the variant and an inner predicate in one call:

fn main() {
    let ok_val: Result<i32, &str> = Ok(42);
    let err_val: Result<i32, &str> = Err("timeout");

    println!("Ok and > 20:       {}", ok_val.is_ok_and(|x| x > 20));
    println!("Err is 'timeout':  {}", err_val.is_err_and(|e| e == "timeout"));
}
Ok and > 20:       true
Err is 'timeout':  true