In Rust programs, iterators are often used to modify or generate data. But to have a more usable collection, like a Vec
or String
, we must convert the iterator.
With collect, we have a powerful built-in function that converts an iterator into a type like a Vec
or String
. Collect can perform many conversions.
String
exampleSuppose we have an iterator of char
values—for example, by having a Vec
of chars that we call iter()
upon. We can collect()
these into a String
.
String
.fn main() { let values: Vec<char> = vec!['a', 'b', 'c']; // Convert a vector of chars into a string. let result = values.iter().collect::<String>(); println!("{result}"); }abc
The map()
function can be used to transform the elements in an iterator with a function argument. Here we uppercase all the strings in a iterator of strings (created by iter
).
collect()
function to convert the result of map into a Vector
of Strings.fn main() { let mut values = Vec::new(); values.push("bird"); values.push("cat"); // Map a vector of strings into uppercase strings. // Then collect the result of map into a vector of strings. let result = values.iter().map(|a| a.to_uppercase()).collect::<Vec<String>>(); println!("{:?}", result); }["BIRD", "CAT"]
In Rust we often use the enumerate()
function to add indexes to an iterator. Each returned element has an index and the original value in a tuple.
collect()
on the result of enumerate()
to transform a vector into a vector of 2-element tuples.fn main() { let values = vec!["abc".to_string(), "def".to_string()]; // Convert string vector into a vector of tuples of index, String pairs. let result = values.iter().enumerate().collect::<Vec<(usize, &String)>>(); println!("{:?}", result); }[(0, "abc"), (1, "def")]
Rust supports the TurboFish syntax, which is a way to cast the result of a function in its calling location. Here we examine this syntax form.
collect()
to cast the result of the rev()
function call.collect()
in the same way, but avoid TurboFish and specify the type of the "result2" local variable.fn main() { let numbers = vec![10, 20, 30]; // Version 1: use collect with TurboFish. let result = numbers.iter().rev().collect::<Vec<&usize>>(); println!("{:?}", result); // Version 2: use collect with type annotation. let result2: Vec<&usize> = numbers.iter().rev().collect(); println!("{:?}", result2); }[30, 20, 10] [30, 20, 10]
How can we use collect()
to optimize Rust programs? With collect, we can store the results of an iterator like bytes()
in a vector.
string
directly, and sums the values of the bytes.collect()
from the bytes of the string
. It also sums the bytes.u8
values created by calling collect.use std::time::*; fn main() { let source = "bird is blue"; let v = source.bytes().collect::<Vec<u8>>(); // Version 1: loop over bytes iterator. let t0 = Instant::now(); let mut sum0 = 0; for _ in 0..1000000000 { for b in source.bytes() { sum0 += b as usize; } } println!("{:?}", t0.elapsed()); // Version 2: loop over bytes vector. let t1 = Instant::now(); let mut sum1 = 0; for _ in 0..1000000000 { for &b in &v { sum1 += b as usize; } } println!("{:?}", t1.elapsed()); println!("{}", sum0); println!("{}", sum1); }4.413035666s bytes() 156.269833ms &v 1125000000000 1125000000000
Split
When calling the split()
function on a string
in Rust, we often want to collect()
the results. But if we can avoid doing this, often the code will run faster.
Collect()
is a powerful and general-purpose conversion function for iterators. It is used at the end of a chain of iterator function calls to convert the result into a more usable type.