ExamplesBy LevelBy TopicLearning Paths
046 Intermediate

046 — Result Map

Functional Programming

Tutorial Video

Text description (accessibility)

This video demonstrates the "046 — Result Map" functional Rust example. Difficulty level: Intermediate. Key concepts covered: Functional Programming. `Result::map` transforms the success value inside `Ok` while leaving `Err` unchanged — the functor operation for `Result`. Key difference from OCaml: 1. **`map_err` naming**: Rust: `map_err`. OCaml: `Result.map_error`. The same operation — transforms the `Err`/`Error` branch.

Tutorial

The Problem

Result::map transforms the success value inside Ok while leaving Err unchanged — the functor operation for Result. It is the equivalent of Option::map but for the two-channel success/failure type. Together with map_err, it enables transformation of both channels: map for the success value, map_err for the error value.

This pattern is essential for adapting between different result types in a pipeline. A library returns Result<i32, LibError>; your code needs Result<String, AppError>. Use result.map(|n| n.to_string()).map_err(AppError::from_lib) to adapt both sides without unwrapping.

🎯 Learning Outcomes

  • • Use result.map(|v| transform(v)) to transform the Ok value
  • • Use result.map_err(|e| convert(e)) to transform the Err value
  • • Chain map calls for sequential transformations on the success path
  • • Use map to add context: result.map(|v| (v, metadata))
  • • Understand that map is equivalent to and_then(|v| Ok(transform(v)))
  • • Use Result::map(f) to transform Ok(x) to Ok(f(x)) while passing Err(e) through unchanged
  • • Use Result::map_err(g) to transform the error value — apply when converting between error types
  • Code Example

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

    Key Differences

  • **map_err naming**: Rust: map_err. OCaml: Result.map_error. The same operation — transforms the Err/Error branch.
  • Consuming vs borrowing: Rust's result.map(f) consumes the result by value. Use result.as_ref().map(f) to map over Result<&T, &E> without consuming. OCaml's GC handles this transparently.
  • Type inference: Both infer the output type of the mapped function. Rust requires explicit map_err call to change error types; OCaml's structural typing can sometimes infer error type changes without explicit conversion.
  • **map vs and_then**: map cannot fail — the function returns U, not Result<U, E>. If the transformation can fail, use and_then instead.
  • **Result::map only transforms Ok:** Errors pass through unchanged. result.map(f) applies f only if result is Ok(v) — otherwise returns the Err(e) unchanged. This is the Functor instance for Result.
  • **map_err for error transformation:** result.map_err(f) applies f to the error value — the mirror of map. Useful for converting between error types.
  • **OCaml Result.map:** Result.map f result applies f to Ok values. Result.map_error f result transforms the error. Both are OCaml 4.08+ standard library functions.
  • Chaining: .map().map_err() can be chained to transform both success and error paths without nested match expressions.
  • OCaml Approach

    OCaml's Result.map f r: let map f = function Ok x -> Ok (f x) | Error e -> Error e. Result.map_error f r maps the error. Pipe style: parse_int s |> Result.map (fun n -> n * 2) |> Result.map string_of_int. OCaml 4.08+ provides Result.map and Result.map_error. Earlier: use pattern matching or define them yourself.

    Full Source

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

    Deep Comparison

    OCaml vs Rust: Result 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

  • Double map: Write transform(r: Result<i32, String>) -> Result<String, String> that doubles the int and converts to string on success, and prepends "Error: " to error messages. Use both map and map_err.
  • Normalization pipeline: Given a Result<&str, IoError>, write a chain that trims whitespace, parses as float, multiplies by 100, and rounds to int. Each step uses map.
  • Split channels: Given Vec<Result<i32, String>>, use partition to split into (Vec<i32>, Vec<String>) of successes and errors. This is the partition_result pattern.
  • Map both sides: Write bimap<T, U, E, F>(f: impl Fn(T) -> U, g: impl Fn(E) -> F, result: Result<T, E>) -> Result<U, F> — transform both the success and error simultaneously.
  • Apply: Implement result_apply<T, U, E: Clone>(f: Result<impl Fn(T) -> U, E>, arg: Result<T, E>) -> Result<U, E> — the applicative <*> for Result.
  • Open Source Repos