Rust has a very common iterator pattern that looks like iter().map(...).collect(). There are many other iterator combinators, but this particular shape appears frequently in Rust code I have seen – which, admittedly, is not a lot, but still /shrugs/.
- The
iter()method creates an iterator that references the elements of a collection. - The
map()takes a function that transforms each element of the iterator and returns a new iterator. - The
collect()trasforms the iterator into a collection.
Thus, the following:
// capitalize is redacted
fn main() {
let v = vec!["foo", "bar", "baz"];
let upper_v = v.iter().map(|s| capitalize(s)).collect::<Vec<String>>();
println!("{:?}", upper_v);
}Will print:
["Foo", "Bar", "Baz"]The good is that it’s fully typed (see how I did to specify the <Vec<String>> there?), it’s very compact and clear, it compiles all the iterator steps into a single optimized loop…and also, it’s ✨ lazy ✨
Which means that we can do something like this:
nums.iter()
.filter(|x| x % 2 == 0)
.map(|x| x * x)
.take(5)
.collect::<Vec<_>>();where the take(5) is slicing the iterator to take only the first 5 values, and only then we collect. Which has the great advantage that the collection is “concretized” on the last step, therefore we don’t waste time by computing the square of every even number in nums and slicing afterwards, but instead we bake the slicing into the “recipe” of the iterator, if it makes sense.