How to iterate over two elements in a collection stepping by one using Iterator?

Viewed 5787

Suppose we have a vector:

let a = vec![1, 2, 3];

What is the best and shortest way to iterate over the elements so, that in the first iteration I receive a tuple of (1, 2), and in the next iteration - (2, 3), until there are no elements, so without producing the (3, None) or anything like that? It seems that a.chunks(2) is a bit different, it steps by two, while I need to step by one over every two consecutive elements in a collection.

2 Answers

There are multiple ways to do so:

Using the standard library:

.zip

let result = a.iter()
    .zip(a.iter().skip(1))
    .inspect(|(a, b)| println!("a: {}, b: {}", a, b)
    .collect::<Vec<_>>();

.windows

let result = a.windows(2)
    .inspect(|w| println!("a: {}, b: {}", w[0], w[1]))
    .collect::<Vec<_>>();

Worth noting, in case this matters, is that windows iterates over subslices, so unlike the above method, collect() in this case will not give you a Vec of tuples.

Using the Itertools crate:

.tuple_windows

use itertools::Itertools;

let result = a.iter()
    .tuple_windows()
    .inspect(|(a, b)| println!("a: {}, b: {}", a, b))
    .collect::<Vec<_>>();

All of the above methods will print the following:

a: 1, b: 2
a: 2, b: 3

As a bonus, Itertools as of recent also has a .circular_tuple_windows(), which performs an extra iteration by including the last (3) and first (1) element:

use itertools::Itertools;

let result = a.iter()
    .circular_tuple_windows()
    .inspect(|(a, b)| println!("a: {}, b: {}", a, b))
    .collect::<Vec<_>>();
a: 1, b: 2
a: 2, b: 3
a: 3, b: 1

With the nightly-only (as of early 2021) array_windows feature, you can iterate over length-2 array references:

for [a, b] in arg.array_windows() {
    println!("a: {}, b: {}", a, b);
}

Playground

Note the use of a slice pattern [a, b], not a tuple pattern (a, b).

On stable you can achieve the same thing with windows and try_into, but it's not as clean:

use std::convert::TryInto;

for s in arg.windows(2) {
    let [a, b]: [i32; 2] = s.try_into().unwrap();
    println!("a: {}, b: {}", a, b);
}
Related