Need to transform each element into multiple items and collect them all into a flat sequence? flat_map combines map and flatten into a single, expressive call.
The nested iterator problem
Say you have a list of sentences and want all individual words. The naive approach with map gives you an iterator of iterators:
1
2
3
4
5
6
7
8
| let sentences = vec!["hello world", "foo bar baz"];
// map gives us an iterator of Split iterators — not what we want
let nested: Vec<Vec<&str>> = sentences.iter()
.map(|s| s.split_whitespace().collect())
.collect();
assert_eq!(nested, vec![vec!["hello", "world"], vec!["foo", "bar", "baz"]]);
|
You could chain .map().flatten(), but flat_map does both at once:
1
2
3
4
5
6
7
| let sentences = vec!["hello world", "foo bar baz"];
let words: Vec<&str> = sentences.iter()
.flat_map(|s| s.split_whitespace())
.collect();
assert_eq!(words, vec!["hello", "world", "foo", "bar", "baz"]);
|
Expanding one-to-many relationships
flat_map shines when each input element maps to zero or more outputs. Think of it as a one-to-many transform:
1
2
3
4
5
6
7
8
| let numbers = vec![1, 2, 3];
// Each number expands to itself and its double
let expanded: Vec<i32> = numbers.iter()
.flat_map(|&n| vec![n, n * 2])
.collect();
assert_eq!(expanded, vec![1, 2, 2, 4, 3, 6]);
|
Since flat_map’s closure can return an empty iterator, it naturally combines filtering and mapping — just return None or Some:
1
2
3
4
5
6
7
| let inputs = vec!["42", "not_a_number", "7", "oops", "13"];
let parsed: Vec<i32> = inputs.iter()
.flat_map(|s| s.parse::<i32>().ok())
.collect();
assert_eq!(parsed, vec![42, 7, 13]);
|
This works because Option implements IntoIterator — Some(x) yields one item, None yields zero. It’s equivalent to filter_map, but flat_map generalizes to any iterator, not just Option.
Traversing nested structures
Got a tree-like structure? flat_map lets you drill into children naturally:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| struct Team {
name: &'static str,
members: Vec<&'static str>,
}
let teams = vec![
Team { name: "backend", members: vec!["Alice", "Bob"] },
Team { name: "frontend", members: vec!["Carol"] },
Team { name: "devops", members: vec!["Dave", "Eve", "Frank"] },
];
let all_members: Vec<&str> = teams.iter()
.flat_map(|team| team.members.iter().copied())
.collect();
assert_eq!(all_members, vec!["Alice", "Bob", "Carol", "Dave", "Eve", "Frank"]);
|
flat_map vs map + flatten
They’re semantically identical — flat_map(f) is just map(f).flatten(). But flat_map reads better and signals your intent: “each element produces multiple items, and I want them all in one sequence.”
1
2
3
4
5
6
7
8
| let data = vec![vec![1, 2], vec![3], vec![4, 5, 6]];
// These are equivalent:
let a: Vec<i32> = data.iter().flat_map(|v| v.iter().copied()).collect();
let b: Vec<i32> = data.iter().map(|v| v.iter().copied()).flatten().collect();
assert_eq!(a, b);
assert_eq!(a, vec![1, 2, 3, 4, 5, 6]);
|
flat_map has been stable since Rust 1.0 — it’s a fundamental iterator combinator that replaces nested loops with clean, composable pipelines.