In Rust, `zip` is an adapter method available on iterators via the `Iterator` trait. Its primary function is to combine two separate iterators into a single new iterator. The new iterator yields pairs (tuples) of elements, taking one element from each of the original iterators at a time.
Key characteristics of `Iterator::zip`:
1. Pairing Elements: It takes two iterators, `self` and `other`, and produces an iterator where each item is a tuple `(self_item, other_item)`. The first element of the tuple comes from the `self` iterator, and the second from the `other` iterator.
2. Shortest Iterator Wins: The zipping process continues only as long as both input iterators are able to yield elements. If one iterator is shorter than the other, `zip` stops when the shorter one is exhausted, effectively truncating the longer iterator. This means the resulting zipped iterator's length will be equal to the length of the shorter input iterator.
3. Ownership and Borrowing: When zipping, you can zip iterators that yield owned values, references, or mutable references. The type of the elements in the resulting tuple will depend on what the original iterators yield (e.g., `&T` or `T`).
4. Common Use Cases: It's incredibly useful for iterating over two related collections or data streams in parallel, performing an action that involves elements from both. For instance, pairing names with ages, keys with values, or indices with items.
5. Inverse: The inverse operation to `zip` is `unzip`, which takes an iterator of tuples and separates it back into two distinct iterators, typically by collecting them into two separate collections.
`zip` is a fundamental building block for many data processing tasks in Rust, providing an elegant and efficient way to synchronize and combine data from multiple sources.
Example Code
fn main() {
// Example 1: Zipping two vectors of the same length
let names = vec!["Alice", "Bob", "Charlie"];
let ages = vec![30, 25, 35];
println!("--- Example 1: Zipping two vectors of the same length ---");
// Using .iter() to get references, so the original vectors are not consumed
for (name, age) in names.iter().zip(ages.iter()) {
println!("{} is {} years old.", name, age);
}
println!();
// Example 2: Zipping with different lengths - the shorter iterator dictates the length
let items = vec!["Apple", "Banana", "Cherry", "Date"];
let prices = vec![1.0, 0.5, 2.0]; // Shorter than 'items'
println!("-- Example 2: Zipping with different lengths ---");
for (item, price) in items.iter().zip(prices.iter()) {
println!("{} costs ${:.2}", item, price);
}
println!("(Notice 'Date' is not processed because prices iterator ended)");
println!();
// Example 3: Zipping ranges (another common use case)
let numbers = 1..=5; // An iterator yielding 1, 2, 3, 4, 5
let letters = vec!['a', 'b', 'c']; // An iterator yielding 'a', 'b', 'c'
println!("--- Example 3: Zipping ranges and vectors ---");
// Here, 'numbers' yields owned i32 values, 'letters.iter()' yields &char
for (num, letter_ref) in numbers.zip(letters.iter()) {
println!("Number: {}, Letter: {}", num, letter_ref);
}
println!("(Numbers 4 and 5 are not processed as letters iterator ended)");
println!();
// Example 4: Collecting zipped results into a new data structure
let ids = vec![101, 102, 103];
let statuses = vec!["active", "inactive", "pending"];
// Using .into_iter() to consume the vectors and move owned values into the tuple
let combined_data: Vec<(i32, &str)> = ids.into_iter().zip(statuses.into_iter()).collect();
println!("--- Example 4: Collecting zipped results ---");
for (id, status) in combined_data {
println!("ID: {}, Status: {}", id, status);
}
}








Iterator::zip (Rust)