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
flatten() as exactly one level of de-nesting for any IntoIterator-yielding iteratorflatten() on Vec<Vec<T>> to produce a flat sequenceflatten() on Vec<Option<T>> to skip None values and collect Tsflat_map(f) as map(f).flatten() — the fundamental identityCode 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
flatten() works on any IntoIterator item — Vec, Option, Result, ranges, custom types; OCaml's List.concat is list-of-lists specific.Option::flatten() method (not the iterator adapter) collapses Option<Option<T>> — a different but related operation.flatten() and List.concat remove exactly one level of nesting — for deeper nesting, compose multiple flatten() calls.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
Vec<Vec<String>> of paragraphs into a flat Vec<String> of sentences.flatten() on Vec<Option<i32>> to collect only the Some values, then compute their sum.flat_map(), then replace flat_map with explicit map().flatten() to verify equivalence.