ZetCode

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.

main.rs
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.

main.rs
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.

main.rs
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.

main.rs
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.

main.rs
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.

main.rs
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.

main.rs
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.

main.rs
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.

main.rs
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

FromIterator trait

In this article we have used the collect method to build vectors, strings, maps, sets, and results from iterators.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Rust tutorials.