Rust collect function
last modified October 25, 2026
In this article we show how to use the collect method in Rust.
The collect method consumes an iterator and builds a collection
from its elements. It is the usual last step of an iterator chain: we create an
iterator, transform it with adapters such as map or
filter, and turn the result into a vector, a string, a map, or
another collection with collect.
The method can produce any type that implements the FromIterator
trait. Because many types do, collect needs to know the target
type. We give it either with a type annotation on the variable or with the
turbofish syntax collect::<T>().
The examples were tested with Rust 1.97 and the 2024 edition. They are run as
Cargo projects with cargo run -q.
Collecting into a vector
The most common target of collect is a Vec.
fn main() {
let vals = [1, 2, 3, 4, 5, 6];
let even: Vec<i32> = vals.iter().filter(|&&x| x % 2 == 0).copied().collect();
println!("{even:?}");
let squares = vals.iter().map(|x| x * x).collect::<Vec<_>>();
println!("{squares:?}");
}
The example filters the even values of an array and computes the squares of all values.
let even: Vec<i32> = vals.iter().filter(|&&x| x % 2 == 0).copied().collect();
The target type is given by the annotation Vec<i32>. The
iter method yields references to the elements; the
copied adapter turns them into i32 values, so that we
get a Vec<i32> rather than a Vec<&i32>.
let squares = vals.iter().map(|x| x * x).collect::<Vec<_>>();
Here the target type is given with the turbofish. The _
placeholder lets the compiler infer the element type.
$ cargo run -q [2, 4, 6] [1, 4, 9, 16, 25, 36]
Iterators are lazy
Iterator adapters do nothing on their own. The work is done only when a
consuming method such as collect pulls the elements through the
chain.
fn main() {
let vals = [1, 2, 3];
let iter = vals.iter().map(|x| {
println!("processing {x}");
x * 10
});
println!("nothing processed yet");
let res: Vec<i32> = iter.collect();
println!("{res:?}");
}
The closure passed to map prints a message for each element it
processes.
let iter = vals.iter().map(|x| {
println!("processing {x}");
x * 10
});
Creating the map adapter does not call the closure. The
iter variable only describes the computation.
let res: Vec<i32> = iter.collect();
The closure runs when collect consumes the iterator. The output
shows that the messages come after the "nothing processed yet" line.
$ cargo run -q nothing processed yet processing 1 processing 2 processing 3 [10, 20, 30]
Collecting into a string
A String can be collected from an iterator of characters or of
string slices.
fn main() {
let word = "falcon";
let upper: String = word.chars().map(|c| c.to_ascii_uppercase()).collect();
println!("{upper}");
let reversed: String = word.chars().rev().collect();
println!("{reversed}");
let words = ["an", "old", "falcon"];
let glued: String = words.iter().copied().collect();
println!("{glued}");
let sentence = words.iter().map(|w| w.to_uppercase()).collect::<Vec<_>>().join(" ");
println!("{sentence}");
}
The example builds four strings with collect.
let upper: String = word.chars().map(|c| c.to_ascii_uppercase()).collect();
The chars method returns an iterator over the characters of the
string. We convert each character to uppercase and collect the characters into
a new String.
let reversed: String = word.chars().rev().collect();
With rev, we collect the characters in reverse order.
let glued: String = words.iter().copied().collect();
Collecting string slices into a String concatenates them without
any separator.
let sentence = words.iter().map(|w| w.to_uppercase()).collect::<Vec<_>>().join(" ");
To put a separator between the parts, we collect them into a vector first and
call join.
$ cargo run -q FALCON noclaf anoldfalcon AN OLD FALCON
Collecting into a HashMap
An iterator of two-element tuples can be collected into a HashMap; the first element of each tuple becomes the key, the second the value.
use std::collections::HashMap;
fn main() {
let users = vec![("John Doe", 34), ("Roger Roe", 54), ("Lucy Smith", 26)];
let ages: HashMap<&str, i32> = users.into_iter().collect();
println!("{}", ages["Roger Roe"]);
let words = ["sky", "forest", "cup", "water"];
let lengths: HashMap<_, _> = words.iter().map(|w| (*w, w.len())).collect();
let mut keys: Vec<_> = lengths.keys().collect();
keys.sort();
for k in keys {
println!("{k}: {}", lengths[k]);
}
}
We create two hash maps: one from a vector of pairs and one from words and their lengths.
let ages: HashMap<&str, i32> = users.into_iter().collect();
The into_iter method consumes the vector and yields its tuples by
value. They are collected into a map from names to ages.
let lengths: HashMap<_, _> = words.iter().map(|w| (*w, w.len())).collect();
The map adapter creates the key-value tuples. With
HashMap<_, _>, we only state the collection type; the key and
value types are inferred.
let mut keys: Vec<_> = lengths.keys().collect(); keys.sort();
A HashMap does not keep any particular order and the order changes
between runs. To print the entries in a stable order, we collect the keys into
a vector and sort it.
$ cargo run -q 54 cup: 3 forest: 6 sky: 3 water: 5
Collecting into sets and ordered maps
Other collections of the standard library implement FromIterator
too.
use std::collections::{BTreeMap, BTreeSet, HashSet};
fn main() {
let vals = [3, 1, 3, 2, 1, 5, 2];
let unique: HashSet<i32> = vals.iter().copied().collect();
println!("unique values: {}", unique.len());
let sorted: BTreeSet<i32> = vals.iter().copied().collect();
println!("{sorted:?}");
let words = ["sky", "forest", "cup", "water"];
let ordered: BTreeMap<&str, usize> = words.iter().map(|w| (*w, w.len())).collect();
println!("{ordered:?}");
}
The example collects values into a HashSet, a
BTreeSet, and a BTreeMap.
let unique: HashSet<i32> = vals.iter().copied().collect();
A set stores each value only once, so collecting into a HashSet
removes the duplicates. Four distinct values remain.
let sorted: BTreeSet<i32> = vals.iter().copied().collect();
A BTreeSet removes the duplicates as well and keeps the values
sorted.
let ordered: BTreeMap<&str, usize> = words.iter().map(|w| (*w, w.len())).collect();
A BTreeMap keeps its keys sorted, so it can be printed directly
in a predictable order.
$ cargo run -q
unique values: 4
{1, 2, 3, 5}
{"cup": 3, "forest": 6, "sky": 3, "water": 5}
Collecting results
An iterator of Result values can be collected into a
Result of a collection. If all elements are Ok, we
get Ok with the collection; at the first Err, the
collecting stops and the error is returned.
fn main() {
let good = ["1", "2", "3"];
let bad = ["1", "two", "3"];
let res: Result<Vec<i32>, _> = good.iter().map(|s| s.parse::<i32>()).collect();
println!("{res:?}");
let res: Result<Vec<i32>, _> = bad.iter().map(|s| s.parse::<i32>()).collect();
println!("{res:?}");
let nums: Vec<i32> = bad.iter().filter_map(|s| s.parse().ok()).collect();
println!("{nums:?}");
}
We parse two arrays of strings into integers.
let res: Result<Vec<i32>, _> = good.iter().map(|s| s.parse::<i32>()).collect();
The parse method returns a Result for each string.
All three strings are valid numbers, so we get Ok with a vector.
let res: Result<Vec<i32>, _> = bad.iter().map(|s| s.parse::<i32>()).collect();
The string "two" cannot be parsed. The result is the parse error; the valid
values are discarded. This all-or-nothing behaviour works well with the
? operator.
let nums: Vec<i32> = bad.iter().filter_map(|s| s.parse().ok()).collect();
If we want to keep the valid values and skip the invalid ones, we use
filter_map. The ok method turns a
Result into an Option, and filter_map
drops the None values.
$ cargo run -q
Ok([1, 2, 3])
Err(ParseIntError { kind: InvalidDigit })
[1, 3]
Collecting options
The same works for Option: an iterator of options is collected
into Some collection if there is no None.
fn main() {
let words = ["sky", "forest", "cup"];
let all: Option<Vec<char>> = words.iter().map(|w| w.chars().nth(3)).collect();
println!("{all:?}");
let short: Option<Vec<char>> = words.iter().map(|w| w.chars().next()).collect();
println!("{short:?}");
}
We take the fourth and the first character of each word.
let all: Option<Vec<char>> = words.iter().map(|w| w.chars().nth(3)).collect();
The nth method returns None if the word is too short.
The words "sky" and "cup" have only three characters, so the whole result is
None.
let short: Option<Vec<char>> = words.iter().map(|w| w.chars().next()).collect();
Every word has a first character; we get Some with a vector of
characters.
$ cargo run -q None Some(['s', 'f', 'c'])
Splitting into two collections
The partition and unzip methods are relatives of
collect that build two collections at once.
fn main() {
let vals = [1, -2, 3, -4, 5, 0];
let (positive, other): (Vec<i32>, Vec<i32>) = vals.iter().partition(|&&x| x > 0);
println!("positive: {positive:?}");
println!("other: {other:?}");
let pairs = [("a", 1), ("b", 2), ("c", 3)];
let (letters, nums): (Vec<&str>, Vec<i32>) = pairs.into_iter().unzip();
println!("{letters:?} {nums:?}");
}
We split an array by a condition and a list of pairs into two lists.
let (positive, other): (Vec<i32>, Vec<i32>) = vals.iter().partition(|&&x| x > 0);
The partition method puts the elements for which the predicate
returns true into the first collection and the rest into the
second.
let (letters, nums): (Vec<&str>, Vec<i32>) = pairs.into_iter().unzip();
The unzip method takes an iterator of pairs and collects the
first elements into one collection and the second elements into another.
$ cargo run -q positive: [1, 3, 5] other: [-2, -4, 0] ["a", "b", "c"] [1, 2, 3]
Adding to an existing collection
The collect method always creates a new collection. To add the
elements of an iterator to a collection that already exists, we use
extend.
fn main() {
let mut vals = vec![1, 2, 3];
vals.extend((4..=6).map(|x| x * 10));
println!("{vals:?}");
}
The extend method appends the values produced by the iterator to
the vector.
$ cargo run -q [1, 2, 3, 40, 50, 60]
Source
Iterator::collect documentation
In this article we have used the collect method to build vectors,
strings, maps, sets, and results from iterators.
Author
List all Rust tutorials.