๐Ÿฆ€ 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

042: Option Map

Difficulty: 1 Level: Foundations Transform the value inside an `Option` without unwrapping it โ€” using `.map()`, `.map_or()`, and `filter_map`.

The Problem This Solves

You have an `Option<i64>` and want to double the number if it exists, or leave it as `None` if it doesn't. The naive approach: unwrap, check for None, transform, re-wrap. That's four lines and easy to get wrong. `.map()` does this in one line. It says: "apply this function to the value inside the `Option`, but only if there's a value. If it's `None`, stay `None`." No unwrapping, no `if let`, no boilerplate. Just a clean transformation that threads through the optional. This pattern โ€” transforming a wrapped value without opening the box โ€” is the heart of functional programming with option types. Python's walrus operator `:=`, JavaScript's optional chaining `?.`, and Kotlin's `?.let {}` all approximate this. Rust makes it explicit and composable.

The Intuition

In Python:
x = parse_int("42")
result = x * 2 if x is not None else None
In Rust:
let result = parse_int("42").map(|x| x * 2);
Same idea โ€” "apply a function if the value exists" โ€” but the Rust version is a single expression. Chain multiple `.map()` calls to build a transformation pipeline:
parse_int("42")
 .map(|x| x * 2)   // doubles: Some(84)
 .map(|x| x + 1)   // adds one: Some(85)
If any step encounters `None`, the whole chain short-circuits to `None`. No null checks at each step.

How It Works in Rust

// map: apply a function to Some(x), pass through None
Some(4.0_f64).map(|x| x.sqrt())   // โ†’ Some(2.0)
None::<f64>.map(|x| x.sqrt())     // โ†’ None

// map_or: apply a function to Some(x), use default for None
Some(5_i64).map_or(0, |x| x * 2)  // โ†’ 10
None::<i64>.map_or(0, |x| x)      // โ†’ 0

// filter_map: map + flatten โ€” apply a function that itself returns Option
// Commonly used to extract Some values from a list of Options:
let opts = vec![Some(1), None, Some(3), None, Some(5)];
let values: Vec<i64> = opts.iter().filter_map(|x| *x).collect();
// โ†’ [1, 3, 5]
The mental model:

What This Unlocks

Key Differences

ConceptOCamlRust
Map over option`Option.map f opt``opt.map(\x\f(x))` (method syntax)
Default on None`Option.value ~default opt``opt.map_or(default, f)`
Map + flatten list`List.filter_map f lst``iter.filter_map(f).collect()`
Chaining`Option.map f (Option.map g opt)``opt.map(g).map(f)` (left-to-right)
Type annotationInferred from `f`Inferred from closure return type
// Option Map โ€” 99 Problems #42
// Transform Option values with map, map_or, and_then, or_else.

fn safe_sqrt(x: f64) -> Option<f64> {
    if x < 0.0 { None } else { Some(x.sqrt()) }
}

fn parse_int(s: &str) -> Option<i64> {
    s.trim().parse().ok()
}

/// Double an Option<i64> using map.
fn double_opt(x: Option<i64>) -> Option<i64> {
    x.map(|v| v * 2)
}

/// Map over a list of Options, keeping only the Somes.
fn filter_map_demo(lst: &[Option<i64>]) -> Vec<i64> {
    lst.iter().filter_map(|x| *x).collect()
}

/// Apply a function, fall back to default.
fn map_or_demo(lst: &[&str], default: i64) -> Vec<i64> {
    lst.iter().map(|s| parse_int(s).map_or(default, |v| v * 10)).collect()
}

fn main() {
    // map: transform Some, pass through None
    println!("Some(4.0).map(sqrt)  = {:?}", Some(4.0f64).map(|x| x.sqrt()));
    println!("None.map(sqrt)       = {:?}", None::<f64>.map(|x| x.sqrt()));

    // safe_sqrt via map
    println!("safe_sqrt(9.0)       = {:?}", safe_sqrt(9.0));
    println!("safe_sqrt(-1.0)      = {:?}", safe_sqrt(-1.0));

    // Chaining with map
    let result = parse_int("42").map(|x| x * 2).map(|x| x + 1);
    println!("parse('42')*2+1      = {:?}", result);

    // map_or
    println!("None.map_or(0, |x|x) = {}", None::<i64>.map_or(0, |x| x));
    println!("Some(5).map_or(0,*2) = {}", Some(5i64).map_or(0, |x| x * 2));

    // filter_map
    let opts = vec![Some(1), None, Some(3), None, Some(5)];
    println!("filter_map           = {:?}", filter_map_demo(&opts));

    // map_or on parse
    let strs = vec!["1", "two", "3", "four"];
    println!("map_or parse         = {:?}", map_or_demo(&strs, -1));

    // double_opt
    println!("double Some(7)       = {:?}", double_opt(Some(7)));
    println!("double None          = {:?}", double_opt(None));
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_map_some() {
        assert_eq!(Some(5i64).map(|x| x * 2), Some(10));
    }

    #[test]
    fn test_map_none() {
        assert_eq!(None::<i64>.map(|x| x * 2), None);
    }

    #[test]
    fn test_filter_map() {
        let opts = vec![Some(1), None, Some(3)];
        assert_eq!(filter_map_demo(&opts), vec![1, 3]);
    }

    #[test]
    fn test_safe_sqrt() {
        assert_eq!(safe_sqrt(4.0), Some(2.0));
        assert_eq!(safe_sqrt(-1.0), None);
    }

    #[test]
    fn test_map_or() {
        assert_eq!(Some(3i64).map_or(0, |x| x * 2), 6);
        assert_eq!(None::<i64>.map_or(99, |x| x), 99);
    }
}
(* Option Map *)
(* OCaml 99 Problems #42 *)

(* Implementation for example 42 *)

(* Tests *)
let () =
  (* Add tests *)
  print_endline "โœ“ OCaml tests passed"