#138 May 14, 2026

138. iter::zip — Parallel Iteration Without the Method-Chain Dance

a.iter().zip(b.iter()) makes one collection the star and the other an awkward sidekick. std::iter::zip puts them on equal footing — two arguments, same indent, no method-chain twist.

The Problem

You have two collections that line up element-for-element. Pair them and walk through together:

1
2
3
4
5
6
let names = ["Alice", "Bob", "Carol"];
let ages  = [30, 25, 40];

for (name, age) in names.iter().zip(ages.iter()) {
    println!("{name}: {age}");
}

Functionally fine — but names is the subject of the chain while ages is shoved into an argument slot. When both collections are equally important to the loop, the asymmetry hides intent. Worse, swap one side for an owned value and the call site gets uglier: names.iter().zip(ages.into_iter()).

The Free Function

std::iter::zip is the same combinator, only as a free function. Both iterables sit at the same level:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
use std::iter::zip;

let names = ["Alice", "Bob", "Carol"];
let ages  = [30, 25, 40];

for (name, age) in zip(&names, &ages) {
    println!("{name}: {age}");
}

let pairs: Vec<(&&str, &i32)> = zip(&names, &ages).collect();
assert_eq!(pairs.len(), 3);
assert_eq!(*pairs[0].0, "Alice");
assert_eq!(*pairs[0].1, 30);

Both arguments are IntoIterator, so you can pass owned values, slices, or references directly — no per-side .iter() decoration to keep balanced.

Mixing Owned and Borrowed

The asymmetry of the method form shows up most when one side is owned:

1
2
3
4
5
6
7
8
use std::iter::zip;

let labels = vec!["x", "y", "z"];
let values: [i32; 3] = [10, 20, 30];

// No .iter() / .into_iter() bookkeeping — zip handles both.
let total: i32 = zip(labels, values).map(|(_, v)| v).sum();
assert_eq!(total, 60);

Same IntoIterator rules as a for loop: arrays by value yield items by value, references yield references.

Same Semantics as Iterator::zip

iter::zip(a, b) is exactly a.into_iter().zip(b). It stops at the shorter side and yields (A, B) tuples — no surprises:

1
2
3
4
5
6
7
use std::iter::zip;

let short = [1, 2];
let long  = [10, 20, 30, 40];

let pairs: Vec<(i32, i32)> = zip(short, long).collect();
assert_eq!(pairs, [(1, 10), (2, 20)]);

When to Reach for It

Use iter::zip when both sides are equal citizens of the loop — typical of parallel data, column-by-column processing, or test setup where inputs and expected outputs travel together. Stick with the method form when you’re already deep in an iterator chain and zipping is just one more step. Stable since Rust 1.59.

← Previous 137. Vec::retain_mut — Filter and Edit In Place, In One Pass Next → 139. HashMap::extract_if — Drain Matching Entries Without Losing Them