ExamplesBy LevelBy TopicLearning Paths
042 Intermediate

042 — Option Map

Functional Programming

Tutorial Video

Text description (accessibility)

This video demonstrates the "042 — Option Map" functional Rust example. Difficulty level: Intermediate. Key concepts covered: Functional Programming. `Option::map` applies a function to the value inside a `Some`, leaving `None` unchanged. Key difference from OCaml: 1. **Method vs function**: Rust: `opt.map(f)` (method call). OCaml: `Option.map f opt` (module function, can be partially applied). The result is identical.

Tutorial

The Problem

Option::map applies a function to the value inside a Some, leaving None unchanged. This is the functor operation — it lifts a function f: T -> U into the option context to produce Option<T> -> Option<U>. The key property: None.map(f) == None for any f. This enables transformation of optional values without explicit null-checking.

map is the foundation of the option/maybe monad in functional languages. It enables fluent chains: find_user(id).map(|u| u.name).map(|n| n.to_uppercase()) — each step transforms the value if present. This pattern eliminates deeply nested if let Some(x) = ... chains, making code that handles missing values as readable as code that does not.

🎯 Learning Outcomes

  • • Use opt.map(|x| transform(x)) to transform values inside Option
  • • Understand that map preserves None — it never introduces or removes None
  • • Chain multiple map calls for sequential transformations
  • • Distinguish map from and_then: map keeps the return type in Option, and_then allows the function to return Option
  • • Use map with method references: opt.map(str::to_uppercase)
  • • Apply Option::map(f) to transform Some(x) to Some(f(x)) while passing None through unchanged
  • • Verify the functor laws: map(id) == id and map(f).map(g) == map(|x| g(f(x)))
  • Code Example

    #![allow(clippy::all)]
    // Placeholder — pending conversion

    Key Differences

  • Method vs function: Rust: opt.map(f) (method call). OCaml: Option.map f opt (module function, can be partially applied). The result is identical.
  • Type inference: Rust infers the closure type from context. OCaml infers both argument and return types. Both languages rarely need explicit type annotations on map calls.
  • **as_ref for reference**: Rust's opt.as_ref().map(...) maps over Option<&T> without consuming the option. OCaml's Option.map always borrows by the GC; no explicit as_ref needed.
  • **map vs Option.iter**: Rust's map produces a new Option. Option::iter produces an iterator of 0 or 1 elements. OCaml's Option.iter f opt calls f for side effects, returns unit.
  • Functor law: Option::map satisfies the functor laws: map id = id and map (f ∘ g) = map f ∘ map g. This means map is composable and predictable. OCaml's Option.map satisfies the same laws.
  • **Chaining map:** Multiple .map() calls chain without nesting: opt.map(f).map(g) vs nested match. Each map propagates None automatically — short-circuiting at the first None.
  • **Option.map in OCaml:** Option.map f opt applies f to the value inside opt. Note the argument order: OCaml's Option.map f opt vs Rust's opt.map(f). The data-last convention in OCaml enables partial application: Option.map double is a function from int option to int option.
  • Argument order: OCaml's Option.map f opt takes function first, option second (data-last, for partial application). Rust's opt.map(f) is method-first (data-first). The computation is identical.
  • OCaml Approach

    OCaml's Option.map f opt: let map f = function None -> None | Some x -> Some (f x). The |> pipe makes chains natural: safe_head lst |> Option.map (fun x -> x * 2) |> Option.map string_of_int. OCaml's Option.map is curried — Option.map (fun x -> x + 1) partially applies to create a function option -> option.

    Full Source

    #![allow(clippy::all)]
    // Placeholder — pending conversion

    Deep Comparison

    OCaml vs Rust: Option Map

    Overview

    See the example.rs and example.ml files for detailed implementations.

    Key Differences

    AspectOCamlRust
    Type systemHindley-MilnerOwnership + traits
    MemoryGCZero-cost abstractions
    MutabilityExplicit refmut keyword
    Error handlingOption/ResultResult<T, E>

    See README.md for detailed comparison.

    Exercises

  • Map string: Write shorten(opt: Option<String>) -> Option<String> that truncates the string to 5 characters if Some. Use opt.map(|s| s.chars().take(5).collect()).
  • Nested option: Given Option<Option<i32>>, write flatten_opt(opt: Option<Option<i32>>) -> Option<i32> using .flatten(). Understand why this is not a map.
  • Map error message: Write validate_age(age: Option<i32>) -> Option<String> that returns Some("valid") for ages 0-150 and None otherwise. Use opt.filter(|&a| a >= 0 && a <= 150).map(|_| "valid".to_string()).
  • Double map: Write map2<A, B, C>(f: impl Fn(A, B) -> C, a: Option<A>, b: Option<B>) -> Option<C> that maps a two-argument function over two options, returning None if either is None.
  • Functor law test: Write unit tests verifying the two functor laws for Option::map: (1) map(id) == id and (2) map(f ∘ g) == map(f) ∘ map(g) for specific functions f and g.
  • Open Source Repos