Got an iterator of tuples and need two separate collections? Stop looping and pushing manually — unzip splits pairs into two collections in a single pass.
The problem
You have an iterator that yields pairs — maybe key-value tuples from a computation, or results from enumerate. You need two separate Vecs. The manual approach works, but it’s noisy:
1
2
3
4
5
6
7
8
9
10
11
| let pairs = vec![("Alice", 95), ("Bob", 87), ("Carol", 92)];
let mut names = Vec::new();
let mut scores = Vec::new();
for (name, score) in &pairs {
names.push(*name);
scores.push(*score);
}
assert_eq!(names, vec!["Alice", "Bob", "Carol"]);
assert_eq!(scores, vec![95, 87, 92]);
|
Two mutable Vecs, a loop, manual pushes — all to split pairs apart.
The fix
Iterator::unzip does exactly this in one line:
1
2
3
4
5
6
| let pairs = vec![("Alice", 95), ("Bob", 87), ("Carol", 92)];
let (names, scores): (Vec<&str>, Vec<i32>) = pairs.into_iter().unzip();
assert_eq!(names, vec!["Alice", "Bob", "Carol"]);
assert_eq!(scores, vec![95, 87, 92]);
|
The type annotation on the left tells Rust which collections to build. It works with any types that implement Default + Extend — so Vec, String, HashSet, and more.
Works great with enumerate
Need indices and values in separate collections?
1
2
3
4
5
6
| let fruits = vec!["apple", "banana", "cherry"];
let (indices, items): (Vec<usize>, Vec<&&str>) = fruits.iter().enumerate().unzip();
assert_eq!(indices, vec![0, 1, 2]);
assert_eq!(items, vec![&"apple", &"banana", &"cherry"]);
|
Chain map before unzip to transform on the fly:
1
2
3
4
5
6
7
8
9
| let data = vec![("temp_c", 20.0), ("temp_c", 35.0), ("temp_c", 0.0)];
let (labels, fahrenheit): (Vec<&str>, Vec<f64>) = data
.into_iter()
.map(|(label, c)| (label, c * 9.0 / 5.0 + 32.0))
.unzip();
assert_eq!(labels, vec!["temp_c", "temp_c", "temp_c"]);
assert_eq!(fahrenheit, vec![68.0, 95.0, 32.0]);
|
Unzip into different collection types
Since unzip works with any Default + Extend types, you can collect into mixed collections:
1
2
3
4
5
6
7
8
9
10
| use std::collections::HashSet;
let entries = vec![("admin", "read"), ("admin", "write"), ("user", "read")];
let (roles, perms): (Vec<&str>, HashSet<&str>) = entries.into_iter().unzip();
assert_eq!(roles, vec!["admin", "admin", "user"]);
assert_eq!(perms.len(), 2); // "read" and "write", deduplicated
assert!(perms.contains("read"));
assert!(perms.contains("write"));
|
One side is a Vec, the other is a HashSet — unzip doesn’t care, as long as both sides can extend themselves.
Whenever you’re about to write a loop that pushes into two collections, reach for unzip instead.