๐Ÿฆ€ Functional Rust
๐ŸŽฌ Error Handling in Rust Option, Result, the ? operator, and combinators.
๐Ÿ“ Text version (for readers / accessibility)

โ€ข Option represents a value that may or may not exist โ€” Some(value) or None

โ€ข Result represents success (Ok) or failure (Err) โ€” no exceptions needed

โ€ข The ? operator propagates errors up the call stack concisely

โ€ข Combinators like .map(), .and_then(), .unwrap_or() chain fallible operations

โ€ข The compiler forces you to handle every error case โ€” no silent failures

315: Result ok() and err() Methods

Difficulty: 2 Level: Intermediate `.ok()` and `.err()` convert `Result` into `Option` โ€” use them when you only care about one side of an outcome.

The Problem This Solves

Sometimes you have a `Result<T, E>` but only care about whether it succeeded, not why it failed. Or you're building a collection and want to skip errors silently. Or you have a function returning `Option<T>` that you need to feed from a `Result`-based API. The `match` pattern for these conversions is repetitive and verbose. `Result` has two "projection" methods that collapse it into an `Option`: `.ok()` keeps the success value and discards the error; `.err()` keeps the error and discards the success. They're conceptually symmetric and often used together with `filter_map` on iterators. Understanding these alongside the full `Result` and `Option` method families โ€” `map`, `and_then`, `or_else`, `flatten`, `zip` โ€” eliminates most `match` expressions in favor of clean, chainable code. This example surveys the API that makes Rust's error handling feel like functional programming.

The Intuition

`.ok()` and `.err()` are "I only care about one outcome" methods. When you call `.ok()`, you're saying "give me the value if it worked, or `None` if it didn't โ€” I don't care about the error details." When you call `.err()`, you're saying "give me the error if it failed, or `None` if it succeeded." The rest of the `Result` API follows a consistent algebra: `map` transforms the value, `map_err` transforms the error, `and_then` chains operations (like `flatMap`), `or_else` provides fallbacks, `and`/`or` combine results logically.

How It Works in Rust

let ok: Result<i32, &str> = Ok(42);
let err: Result<i32, &str> = Err("oops");

// .ok() โ†’ Option<T>: keeps value, discards error
ok.ok()   // โ†’ Some(42)
err.ok()  // โ†’ None

// .err() โ†’ Option<E>: keeps error, discards value
ok.err()  // โ†’ None
err.err() // โ†’ Some("oops")

// Common use: filter_map to skip errors
let results = vec![Ok(1), Err("bad"), Ok(3)];
let values: Vec<i32> = results.into_iter().filter_map(|r| r.ok()).collect();
// โ†’ [1, 3]

// Chaining combinators
Ok(5_i32)
 .map(|x| x * 2)          // Ok(10)
 .and_then(|x| if x > 5 { Ok(x) } else { Err("too small") })
 .unwrap_or(0);            // โ†’ 10

What This Unlocks

Key Differences

ConceptOCamlRust
Result โ†’ Option (value)`Option.of_result` / pattern match`result.ok()`
Result โ†’ Option (error)Pattern match`result.err()`
Transform value`Result.map``result.map(f)`
Transform error`Result.map_error``result.map_err(f)`
Chain operations`Result.bind` / `let*``result.and_then(f)`
Fallback on errorManual match`result.or_else(f)`
//! # Exhaustive Result/Option Method Survey
//!
//! Complete reference of `Result<T,E>` and `Option<T>` methods.

/// Demonstrate Result methods
pub fn result_methods() {
    let ok: Result<i32, &str> = Ok(5);
    let err: Result<i32, &str> = Err("bad");

    // Query methods
    let _ = ok.is_ok();
    let _ = ok.is_err();
    let _ = ok.ok();
    let _ = err.err();

    // Transform methods
    let _ = ok.map(|x| x * 2);
    let _ = err.map_err(|e| format!("error: {}", e));
    let _ = ok.map_or(0, |x| x + 1);
    let _ = ok.map_or_else(|_| 0, |x| x);

    // Combinators
    let _ = ok.and(Ok::<i32, &str>(10));
    let _ = err.or(Ok::<i32, &str>(42));
    let _ = ok.and_then(|x| Ok::<i32, &str>(x * 2));
    let _ = err.or_else(|_| Ok::<i32, &str>(99));

    // Unwrap variants
    let _ = ok.unwrap_or(0);
    let _ = ok.unwrap_or_else(|_| 0);
    let _ = ok.unwrap_or_default();
}

/// Demonstrate Option methods
pub fn option_methods() {
    let some: Option<i32> = Some(5);
    let none: Option<i32> = None;

    // Query methods
    let _ = some.is_some();
    let _ = none.is_none();

    // Transform methods
    let _ = some.map(|x| x * 2);
    let _ = some.filter(|&x| x > 3);
    let _ = some.map_or(0, |x| x + 1);

    // Combinators
    let _ = some.and(Some(10));
    let _ = none.or(Some(42));
    let _ = some.and_then(|x| Some(x * 2));
    let _ = none.or_else(|| Some(99));

    // Unwrap variants
    let _ = none.unwrap_or(0);
    let _ = none.unwrap_or_else(|| 99);
    let _ = none.unwrap_or_default();

    // Conversion
    let _ = none.ok_or("missing");
    let _ = Some(Some(42)).flatten();
    let _ = some.zip(Some("hello"));
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_result_map_chain() {
        let r: Result<i32, &str> = Ok(5);
        assert_eq!(r.map(|x| x * 2).map(|x| x + 1), Ok(11));
    }

    #[test]
    fn test_option_and_then_chain() {
        let r = Some(5i32)
            .and_then(|x| if x > 0 { Some(x * 2) } else { None })
            .filter(|&x| x < 20);
        assert_eq!(r, Some(10));
    }

    #[test]
    fn test_result_or_else() {
        let r: Result<i32, &str> = Err("bad");
        assert_eq!(r.or_else(|_| Ok::<i32, &str>(42)), Ok(42));
    }

    #[test]
    fn test_option_zip() {
        assert_eq!(Some(1).zip(Some("a")), Some((1, "a")));
        assert_eq!(Some(1).zip(None::<&str>), None);
    }

    #[test]
    fn test_option_flatten() {
        assert_eq!(Some(Some(42)).flatten(), Some(42));
        assert_eq!(Some(None::<i32>).flatten(), None);
    }
}
(* 315. Exhaustive Result/Option method survey - OCaml *)

let () =
  let ok_5 : (int, string) result = Ok 5 in
  let err : (int, string) result = Error "bad" in
  let some_5 = Some 5 in
  let none : int option = None in

  (* Result methods *)
  Printf.printf "is_ok:   %b %b\n" (Result.is_ok ok_5) (Result.is_ok err);
  Printf.printf "is_error: %b %b\n" (Result.is_error ok_5) (Result.is_error err);
  Printf.printf "map Ok: %s\n"
    (match Result.map (fun x -> x * 2) ok_5 with Ok n -> string_of_int n | Error e -> e);
  Printf.printf "map_error: %s\n"
    (match Result.map_error (fun e -> "prefix: " ^ e) err with
     | Ok n -> string_of_int n | Error e -> e);
  Printf.printf "value: %d\n" (Result.get_ok ok_5);

  (* Option methods *)
  Printf.printf "is_some: %b %b\n" (Option.is_some some_5) (Option.is_some none);
  Printf.printf "value: %d\n" (Option.value some_5 ~default:0);
  Printf.printf "map: %d\n" (Option.fold ~none:0 ~some:(fun x -> x * 2) some_5);
  Printf.printf "bind: %s\n"
    (match Option.bind some_5 (fun x -> if x > 0 then Some (x + 1) else None) with
     | Some n -> string_of_int n | None -> "None")