ExamplesBy LevelBy TopicLearning Paths
272 Intermediate

272: One-Level Flattening with flatten()

Functional Programming

Tutorial

The Problem

Nested collections are ubiquitous: a list of lists of results, a tree traversal producing sub-lists per node, or an iterator of options where only Some values matter. The flatten() adapter removes exactly one level of nesting — turning Iterator<Item = Iterator<Item = T>> into Iterator<Item = T>. It is the foundation for monadic composition and list comprehensions, and is the building block behind flat_map().

🎯 Learning Outcomes

  • • Understand flatten() as exactly one level of de-nesting for any IntoIterator-yielding iterator
  • • Use flatten() on Vec<Vec<T>> to produce a flat sequence
  • • Apply flatten() on Vec<Option<T>> to skip None values and collect Ts
  • • Recognize flat_map(f) as map(f).flatten() — the fundamental identity
  • Code Example

    #![allow(clippy::all)]
    //! 272. One-level flattening with flatten()
    //!
    //! `flatten()` removes exactly one level of iterator nesting.
    
    #[cfg(test)]
    mod tests {
        #[test]
        fn test_flatten_vec_vec() {
            let nested = vec![vec![1i32, 2], vec![3, 4]];
            let flat: Vec<i32> = nested.into_iter().flatten().collect();
            assert_eq!(flat, vec![1, 2, 3, 4]);
        }
    
        #[test]
        fn test_flatten_option() {
            assert_eq!(Some(Some(42i32)).flatten(), Some(42));
            assert_eq!(Some(None::<i32>).flatten(), None);
            assert_eq!(None::<Option<i32>>.flatten(), None);
        }
    
        #[test]
        fn test_flatten_options_in_iter() {
            let v: Vec<Option<i32>> = vec![Some(1), None, Some(3)];
            let result: Vec<i32> = v.into_iter().flatten().collect();
            assert_eq!(result, vec![1, 3]);
        }
    }

    Key Differences

  • Generic nesting: Rust's flatten() works on any IntoIterator item — Vec, Option, Result, ranges, custom types; OCaml's List.concat is list-of-lists specific.
  • Option flattening: Rust's Option::flatten() method (not the iterator adapter) collapses Option<Option<T>> — a different but related operation.
  • One level only: Both flatten() and List.concat remove exactly one level of nesting — for deeper nesting, compose multiple flatten() calls.
  • Performance: Both implementations are lazy in Rust (iterator) and strict in OCaml (list operations build new lists).
  • OCaml Approach

    OCaml provides List.concat for flattening a list of lists, and List.filter_map Fun.id for filtering options. The Seq module provides Seq.flat_map for lazy flattening:

    let flat = List.concat [[1;2]; [3;4]]
    (* [1; 2; 3; 4] *)
    
    let values = List.filter_map Fun.id [Some 1; None; Some 3]
    (* [1; 3] *)
    

    Full Source

    #![allow(clippy::all)]
    //! 272. One-level flattening with flatten()
    //!
    //! `flatten()` removes exactly one level of iterator nesting.
    
    #[cfg(test)]
    mod tests {
        #[test]
        fn test_flatten_vec_vec() {
            let nested = vec![vec![1i32, 2], vec![3, 4]];
            let flat: Vec<i32> = nested.into_iter().flatten().collect();
            assert_eq!(flat, vec![1, 2, 3, 4]);
        }
    
        #[test]
        fn test_flatten_option() {
            assert_eq!(Some(Some(42i32)).flatten(), Some(42));
            assert_eq!(Some(None::<i32>).flatten(), None);
            assert_eq!(None::<Option<i32>>.flatten(), None);
        }
    
        #[test]
        fn test_flatten_options_in_iter() {
            let v: Vec<Option<i32>> = vec![Some(1), None, Some(3)];
            let result: Vec<i32> = v.into_iter().flatten().collect();
            assert_eq!(result, vec![1, 3]);
        }
    }
    ✓ Tests Rust test suite
    #[cfg(test)]
    mod tests {
        #[test]
        fn test_flatten_vec_vec() {
            let nested = vec![vec![1i32, 2], vec![3, 4]];
            let flat: Vec<i32> = nested.into_iter().flatten().collect();
            assert_eq!(flat, vec![1, 2, 3, 4]);
        }
    
        #[test]
        fn test_flatten_option() {
            assert_eq!(Some(Some(42i32)).flatten(), Some(42));
            assert_eq!(Some(None::<i32>).flatten(), None);
            assert_eq!(None::<Option<i32>>.flatten(), None);
        }
    
        #[test]
        fn test_flatten_options_in_iter() {
            let v: Vec<Option<i32>> = vec![Some(1), None, Some(3)];
            let result: Vec<i32> = v.into_iter().flatten().collect();
            assert_eq!(result, vec![1, 3]);
        }
    }

    Exercises

  • Flatten a Vec<Vec<String>> of paragraphs into a flat Vec<String> of sentences.
  • Use flatten() on Vec<Option<i32>> to collect only the Some values, then compute their sum.
  • Implement a tree traversal that yields child nodes at each level using flat_map(), then replace flat_map with explicit map().flatten() to verify equivalence.
  • Open Source Repos