ExamplesBy LevelBy TopicLearning Paths
271 Intermediate

271: Transform-and-Find with find_map()

Functional Programming

Tutorial

The Problem

A very common pattern is: try to convert each element into a useful form, stop at the first success, and return it. For example, parse numbers from strings until one succeeds, look up each key in several registries until a hit, or try multiple fallback strategies until one works. The naive approach chains map() and find(), but this creates intermediate Option values. The find_map(f) adapter fuses these into a single lazy operation: find the first Some(...) result from applying f.

🎯 Learning Outcomes

  • • Understand find_map(f) as fused map(f).flatten().next() — find the first Some from f
  • • Recognize the "try each, return first success" pattern that find_map optimally handles
  • • Use find_map() to parse heterogeneous data, looking for the first valid interpretation
  • • Distinguish from filter_map(): find_map stops at first success, filter_map collects all
  • Code Example

    #![allow(clippy::all)]
    //! 271. Transform-and-find with find_map()
    //!
    //! `find_map(f)` finds the first `Some(...)` result — single pass, lazy.
    
    #[cfg(test)]
    mod tests {
        #[test]
        fn test_find_map_parse() {
            let strings = ["foo", "bar", "42", "baz"];
            let result = strings.iter().find_map(|s| s.parse::<i32>().ok());
            assert_eq!(result, Some(42));
        }
    
        #[test]
        fn test_find_map_none() {
            let strings = ["foo", "bar"];
            let result = strings.iter().find_map(|s| s.parse::<i32>().ok());
            assert_eq!(result, None);
        }
    
        #[test]
        fn test_find_map_first_match() {
            let nums = [1i32, 2, 3, 4, 5];
            let result = nums
                .iter()
                .find_map(|&x| if x > 3 { Some(x * 10) } else { None });
            assert_eq!(result, Some(40));
        }
    }

    Key Differences

  • Standard library parity: Both Rust and OCaml (4.10+) provide find_map as a standard function with identical semantics.
  • Lazy: Both implementations stop at the first Some — crucial for expensive operations like network lookups or file parsing.
  • vs filter_map: find_map returns Option<B> (first success); filter_map returns Iterator<Item=B> (all successes).
  • Combinatorial search: Useful in plugin systems, format auto-detection, and fallback chains where you try until one works.
  • OCaml Approach

    OCaml's List.find_map (standard since OCaml 4.10) is exactly equivalent:

    let result = List.find_map (fun s ->
      match int_of_string_opt s with
      | Some n -> Some n
      | None -> None
    ) ["foo"; "bar"; "42"; "baz"]
    (* Some 42 *)
    

    This is one of the cleanest analogies between the two languages — both provide find_map as a standard library function.

    Full Source

    #![allow(clippy::all)]
    //! 271. Transform-and-find with find_map()
    //!
    //! `find_map(f)` finds the first `Some(...)` result — single pass, lazy.
    
    #[cfg(test)]
    mod tests {
        #[test]
        fn test_find_map_parse() {
            let strings = ["foo", "bar", "42", "baz"];
            let result = strings.iter().find_map(|s| s.parse::<i32>().ok());
            assert_eq!(result, Some(42));
        }
    
        #[test]
        fn test_find_map_none() {
            let strings = ["foo", "bar"];
            let result = strings.iter().find_map(|s| s.parse::<i32>().ok());
            assert_eq!(result, None);
        }
    
        #[test]
        fn test_find_map_first_match() {
            let nums = [1i32, 2, 3, 4, 5];
            let result = nums
                .iter()
                .find_map(|&x| if x > 3 { Some(x * 10) } else { None });
            assert_eq!(result, Some(40));
        }
    }
    ✓ Tests Rust test suite
    #[cfg(test)]
    mod tests {
        #[test]
        fn test_find_map_parse() {
            let strings = ["foo", "bar", "42", "baz"];
            let result = strings.iter().find_map(|s| s.parse::<i32>().ok());
            assert_eq!(result, Some(42));
        }
    
        #[test]
        fn test_find_map_none() {
            let strings = ["foo", "bar"];
            let result = strings.iter().find_map(|s| s.parse::<i32>().ok());
            assert_eq!(result, None);
        }
    
        #[test]
        fn test_find_map_first_match() {
            let nums = [1i32, 2, 3, 4, 5];
            let result = nums
                .iter()
                .find_map(|&x| if x > 3 { Some(x * 10) } else { None });
            assert_eq!(result, Some(40));
        }
    }

    Exercises

  • Given a list of user IDs, use find_map() to return the first user that exists in a HashMap lookup.
  • Try to parse a string first as i64, then as f64, then as bool, using find_map over a list of parsing functions.
  • Implement find_map from scratch using only map(), filter(), and next(), then verify it produces identical results.
  • Open Source Repos