Map and Filter
🎯 In Python, list comprehensions handle both transforming and filtering in one readable expression:
squares = [x**2 for x in numbers]
evens = [x for x in numbers if x % 2 == 0]
lengths = [len(s) for s in words if s]
Python also has map() and filter() builtins, but list comprehensions are more idiomatic. In Rust, iterator methods like .map() and .filter() are the idiomatic approach — they replace both loops and comprehensions.
.map() — transform each element
.map() takes a closure and applies it to every element, producing a new iterator:
let doubled: Vec<i32> = vec![1, 2, 3]
.iter()
.map(|x| x * 2)
.collect();
// [2, 4, 6]
This is like [x * 2 for x in vec] in Python. The closure receives each element (here &i32 since we used .iter()), and the result becomes the new element.
.filter() — keep elements matching a condition
.filter() takes a predicate (a closure returning bool) and keeps only elements where it returns true:
let evens: Vec<&i32> = vec![1, 2, 3, 4]
.iter()
.filter(|x| *x % 2 == 0)
.collect();
// [&2, &4]
Watch the references. When you call .filter() on an .iter(), there's a double reference: .iter() yields &i32, and .filter() passes each element by reference, so the closure gets &&i32. You'll need to dereference with * or use pattern matching (|&&x|) to access the value. This is a common stumbling block — if the compiler complains about applying % to &&i32, this is why.
To get owned values out, you can use .copied() after filtering (for types that implement Copy):
let evens: Vec<i32> = vec![1, 2, 3, 4]
.iter()
.filter(|x| **x % 2 == 0)
.copied()
.collect();
// [2, 4] — now Vec<i32>, not Vec<&i32>
.filter_map() — filter and transform in one step
Sometimes you want to try a transformation that might fail, keeping only the successes. In Python:
# keep only non-empty first characters
firsts = [s[0] for s in ["hello", "", "world"] if s]
# ['h', 'w']
Rust's .filter_map() combines .filter() and .map() — the closure returns Option<T>, and only Some values are kept:
let first_chars: Vec<char> = vec!["hello", "", "world"]
.iter()
.filter_map(|s| s.chars().next())
.collect();
// ['h', 'w'] — empty string yields None, so it's skipped
The closure returns Option<T> — Some(value) is kept, None is dropped. This is cleaner than a separate .filter() + .map() chain when the transform itself tells you whether to keep the element.
Chaining
All these methods are lazy iterators, so you can chain them freely. Nothing runs until a consuming adapter (.collect(), .sum(), etc.) drives the chain:
let result: Vec<i32> = numbers.iter()
.filter(|&&x| x > 0)
.map(|&x| x * x)
.collect();
Login to see the full task and start coding.
Topics
This is a premium exercise
Log in to unlock the full exercise and start coding.
Login to access this exercise