๐Ÿฆ€ Functional Rust
๐ŸŽฌ How Rust Iterators Work Lazy evaluation, chaining, collect(), and zero-cost abstractions.
๐Ÿ“ Text version (for readers / accessibility)

โ€ข Iterators are lazy โ€” .map(), .filter(), .take() build a chain but do no work until consumed

โ€ข .collect() triggers evaluation, transforming the chain into a Vec, HashMap, or other collection

โ€ข Zero-cost abstraction: iterator chains compile to the same machine code as hand-written loops

โ€ข .iter() borrows, .into_iter() consumes, .iter_mut() borrows mutably

โ€ข Chaining replaces nested loops with a readable, composable pipeline

259: Flattening with flat_map()

Difficulty: 2 Level: Intermediate Map each element to zero-or-more outputs, collecting them all into a single flat sequence.

The Problem This Solves

Sometimes a transformation produces multiple values per input โ€” or none at all. Splitting sentences into words, expanding a range per number, parsing optional results, extracting key-value pairs. A plain `map()` leaves you with nested collections: `Vec<Vec<&str>>`, `Vec<Option<i32>>`. You then need a second pass to flatten them. In Python you'd write a list comprehension with two `for` clauses: `[word for sentence in sentences for word in sentence.split()]`. In OCaml, `List.concat_map` does the same. In Rust, `flat_map()` is exactly that โ€” map then flatten, in a single lazy pass. The zero-output case is especially powerful: `flat_map(|s| s.parse::<i32>())` silently drops parse failures because `Result` implements `IntoIterator` (yielding one element on `Ok`, zero on `Err`). This replaces a `filter` followed by an `unwrap`.

The Intuition

`flat_map(f)` applies `f` to each element (where `f` returns something iterable), then concatenates all the results into one sequence. It's `map` + `flatten` in a single adapter โ€” and it's the iterator monad's `bind` operation.
let sentences = ["hello world", "foo bar"];
let words: Vec<&str> = sentences.iter()
 .flat_map(|s| s.split_whitespace())
 .collect();
// โ†’ ["hello", "world", "foo", "bar"]

How It Works in Rust

// Expand each number into a range
let nums = [1i32, 2, 3, 4];
let expanded: Vec<i32> = nums.iter().flat_map(|&n| 0..n).collect();
// โ†’ [0, 0,1, 0,1,2, 0,1,2,3]

// Silently discard parse failures (Result yields 1 or 0 elements)
let strings = ["1", "two", "3", "four", "5"];
let valid: Vec<i32> = strings.iter()
 .flat_map(|s| s.parse::<i32>())  // Err variants produce 0 items
 .collect();
// โ†’ [1, 3, 5]

// CSV: outer iterator = lines, inner iterator = fields
let csv = "1,2,3\n4,5,6";
let values: Vec<&str> = csv.lines()
 .flat_map(|line| line.split(','))
 .collect();
// โ†’ ["1","2","3","4","5","6"]
`flat_map(f)` is exactly equivalent to `.map(f).flatten()` โ€” use whichever reads more clearly.

What This Unlocks

Key Differences

ConceptOCamlRust
Map + flatten`List.concat_map f lst``iter.flat_map(f)`
Skip `None`/errorsManual `filter_map` pattern`flat_map(\x\option_or_result)`
Lazy evaluationNo (strict lists)Yes โ€” produces one element at a time
Nesting depthFlattens all levels with `List.flatten`Flattens exactly one level
//! 259. Flattening with flat_map()
//!
//! `flat_map(f)` = `map(f).flatten()` โ€” the iterator monad's bind operation.

fn main() {
    let words = ["hello", "world"];
    let bytes: Vec<u8> = words.iter().flat_map(|w| w.bytes()).collect();
    println!("Bytes count: {}", bytes.len());

    let nums = [1i32, 2, 3, 4];
    let expanded: Vec<i32> = nums.iter().flat_map(|&n| 0..n).collect();
    println!("Expanded: {:?}", expanded);

    // Filter and transform simultaneously
    let strings = ["1", "two", "3", "four", "5"];
    let valid: Vec<i32> = strings.iter()
        .flat_map(|s| s.parse::<i32>())
        .collect();
    println!("Valid numbers: {:?}", valid);

    // Flatten nested structure
    let sentences = ["the quick brown", "fox jumps over"];
    let all_words: Vec<&str> = sentences.iter()
        .flat_map(|s| s.split_whitespace())
        .collect();
    println!("All words: {:?}", all_words);

    // CSV parsing
    let csv = "1,2,3
4,5,6";
    let values: Vec<&str> = csv.lines().flat_map(|line| line.split(',')).collect();
    println!("CSV values: {:?}", values);
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_flat_map_expand() {
        let result: Vec<i32> = [1i32, 2, 3].iter().flat_map(|&n| 0..n).collect();
        assert_eq!(result, vec![0, 0, 1, 0, 1, 2]);
    }

    #[test]
    fn test_flat_map_filter_parse() {
        let strings = ["1", "x", "2", "y", "3"];
        let result: Vec<i32> = strings.iter()
            .flat_map(|s| s.parse::<i32>())
            .collect();
        assert_eq!(result, vec![1, 2, 3]);
    }

    #[test]
    fn test_flat_map_words() {
        let sentences = ["hello world", "foo bar"];
        let words: Vec<&str> = sentences.iter()
            .flat_map(|s| s.split_whitespace())
            .collect();
        assert_eq!(words.len(), 4);
    }
}
(* 259. Flattening with flat_map() - OCaml *)

let () =
  let words = ["hello"; "world"] in
  let chars = List.concat_map (fun w ->
    List.init (String.length w) (fun i -> w.[i])
  ) words in
  List.iter (fun c -> Printf.printf "%c " c) chars;
  print_newline ();

  let nums = [1; 2; 3] in
  let expanded = List.concat_map (fun n -> List.init n Fun.id) nums in
  Printf.printf "%s\n" (String.concat ", " (List.map string_of_int expanded));

  let strs = ["1"; "two"; "3"; "four"; "5"] in
  let parsed = List.concat_map (fun s ->
    match int_of_string_opt s with
    | Some n -> [n * 2]
    | None -> []
  ) strs in
  Printf.printf "%s\n" (String.concat ", " (List.map string_of_int parsed))