Iterator::map() / filter()
| Since: | Rust 1.0(2015) |
|---|
Rust iterators provide map() for transforming elements and filter() for selecting elements based on a condition. Both are lazy — they do not execute until a terminal operation such as collect() is called.
Syntax
let doubled: Vec<i32> = vec![1, 2, 3].iter().map(|x| x * 2).collect();
// filter(): Keeps only the elements that satisfy the condition.
let evens: Vec<i32> = vec![1, 2, 3, 4, 5].iter().filter(|x| **x % 2 == 0).collect();
// filter_map(): Combines filter and map — discards None and unwraps Some values.
let parsed: Vec<i32> = vec!["1", "abc", "3"]
.iter()
.filter_map(|s| s.parse::<i32>().ok())
.collect();
// Methods can be chained together.
let result: Vec<i32> = (1..=10)
.filter(|x| x % 2 == 0) // Keep even numbers only.
.map(|x| x * x) // Square each element.
.collect();
Method List
| Method | Description |
|---|---|
| iter.map(|x| ...) | Returns a new iterator with each element transformed by the closure. |
| iter.filter(|x| ...) | Returns an iterator containing only the elements that satisfy the condition. |
| iter.filter_map(|x| ...) | Returns an iterator that transforms elements and keeps only the Some values. |
| iter.map_while(|x| ...) | Returns an iterator that continues transforming elements as long as the closure returns Some. |
| iter.flat_map(|x| ...) | Returns an iterator that transforms each element and flattens the nested results. |
| iter.take_while(|x| ...) | Returns an iterator that yields elements as long as the condition is true. |
| iter.skip_while(|x| ...) | Returns an iterator that skips elements as long as the condition is true. |
| iter.cloned() | Clones each element of a reference iterator, producing an iterator of type T. |
| iter.copied() | Copies each element of a reference iterator, producing an iterator of type T (requires the Copy trait). |
Sample Code
sample_iter_map_filter.rs
fn main() {
let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// map(): Double every element.
let doubled: Vec<i32> = numbers.iter().map(|x| x * 2).collect();
println!("doubled: {:?}", doubled);
// filter(): Keep only even numbers (the closure receives &&T).
let evens: Vec<&i32> = numbers.iter().filter(|x| **x % 2 == 0).collect();
println!("evens: {:?}", evens);
// Using copied() yields T instead of &&T.
let evens2: Vec<i32> = numbers.iter().copied().filter(|x| x % 2 == 0).collect();
println!("evens2: {:?}", evens2);
// Chaining filter and map.
let result: Vec<i32> = numbers.iter()
.filter(|&&x| x > 5) // Keep numbers greater than 5.
.map(|&x| x * x) // Square each element.
.collect();
println!("filter+map (squares of x>5): {:?}", result);
// filter_map(): Extract only the values that parse successfully.
let strs = vec!["42", "abc", "7", "!", "100"];
let nums: Vec<i32> = strs.iter().filter_map(|s| s.parse().ok()).collect();
println!("filter_map (parsed): {:?}", nums);
// take_while() and skip_while().
let v = vec![1, 2, 3, 10, 4, 5];
let taken: Vec<&i32> = v.iter().take_while(|&&x| x < 5).collect();
println!("take_while (x<5): {:?}", taken); // Stops at 10.
// Apply map to a range.
let squares: Vec<i32> = (1..=5).map(|x| x * x).collect();
println!("squares 1–5: {:?}", squares);
// Convert strings to uppercase.
let words = vec!["hello", "world", "rust"];
let upper: Vec<String> = words.iter().map(|s| s.to_uppercase()).collect();
println!("uppercase: {:?}", upper);
}
Compile with the following command:
rustc iter_map_filter.rs ./iter_map_filter doubled: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] evens: [2, 4, 6, 8, 10] evens2: [2, 4, 6, 8, 10] filter+map (squares of x>5): [36, 49, 64, 81, 100] filter_map (parsed): [42, 7, 100] take_while (x<5): [1, 2, 3] squares 1–5: [1, 4, 9, 16, 25] uppercase: ["HELLO", "WORLD", "RUST"]
Common Mistakes
Common mistake 1: double reference (&&T) in filter() closures
When iterating over a slice or Vec with iter(), the iterator yields &T. The filter() closure receives another layer of reference, giving &&T. Use pattern matching to dereference cleanly.
fn main() {
let numbers = vec![1, 2, 3, 4, 5, 6];
// iter() yields &i32; filter() receives &&i32
let evens: Vec<&i32> = numbers.iter()
.filter(|x| **x % 2 == 0) // double dereference
.collect();
println!("{:?}", evens); // [2, 4, 6]
// Use pattern matching for cleaner code
let evens2: Vec<&i32> = numbers.iter()
.filter(|&&x| x % 2 == 0)
.collect();
println!("{:?}", evens2); // [2, 4, 6]
}
Common mistake 2: iterators are lazy and do nothing without a terminal operation
Rust iterators use lazy evaluation. A map() or filter() call alone does not execute any work. A terminal operation (collect(), for_each(), count(), etc.) is needed to drive execution.
fn main() {
let data = vec![1, 2, 3];
// This does nothing — the closure never runs
let _ = data.iter().map(|x| {
println!("never printed");
x * 2
});
// collect() drives the iterator — the closure runs now
let doubled: Vec<i32> = data.iter()
.map(|x| {
println!("processing: {}", x);
x * 2
})
.collect();
println!("{:?}", doubled); // [2, 4, 6]
}
Overview
Rust iterators use lazy evaluation. Calling map() or filter() does not execute immediately — processing begins only when a terminal operation such as collect(), sum(), or count() is called. This avoids creating intermediate collections and keeps processing efficient.
The closure passed to filter() receives &&T (a reference to a reference). To avoid this double-reference, use iter().copied() or into_iter() to obtain an owning iterator before filtering, which makes the code easier to read.
For terminal operations that collect or aggregate results, see Iterator::collect() / count() / sum().
If you find any errors or copyright issues, please contact us.