HiveBrain v1.2.0
Get Started
← Back to all entries
patternrustModerate

Are there equivalents to slice::chunks/windows for iterators to loop over pairs, triplets etc?

Submitted by: @import:stackoverflow-api··
0
Viewed 0 times
equivalentsslicechunksareiteratorspairsoverfortripletsloop

Problem

It can be useful to iterate over multiple variables at once, overlapping (slice::windows), or not (slice::chunks).

This only works for slices; is it possible to do this for iterators, using tuples for convenience?

Something like the following could be written:

for (prev, next) in some_iter.windows(2) {
    ...
}


If not, could it be implemented as a trait on existing iterators?

Solution

On stable

Since Rust 1.51 this is possible with const generics where the iterator yields constant size arrays [T; N] for any N.

I built the itermore crate which implements both of these, providing the array_chunks() and array_windows() methods under separate extension traits.
use itermore::IterArrayChunks; // 0.7

for [a, b, c] in some_iter.by_ref().array_chunks() {
...
}

let rem = some_iter.into_remainder();

use itermore::IterArrayWindows; // 0.7

for [prev, next] in some_iter.array_windows() {
...
}


Using the example given in the Itertools answer:

use itermore::IterArrayChunks; // 0.7

fn main() {
    let some_iter = vec![1, 2, 3, 4, 5, 6].into_iter();

    for [prev, next] in some_iter.array_chunks() {
        println!("{}--{}", prev, next);
    }
}


This outputs

1--2
3--4
5--6


Most times the array size can be inferred but you can also specific it explicitly. Additionally, any reasonable size N can be used, there is no limit like in the Itertools case.
use itermore::IterArrayWindows; // 0.7

fn main() {
let mut iter = vec![1, 2, 3, 4, 5, 6].into_iter().array_windows::();
println!("{:?}", iter.next());
println!("{:?}", iter.next());
println!("{:?}", iter.next());
}


This outputs

Some([1, 2, 3, 4, 5])
Some([2, 3, 4, 5, 6])
None


Note: array_windows() uses clone to yield elements multiple times so its best used for references and cheap to copy types.
On nightly

The chunks version is now available on nightly under the name array_chunks

#![feature(iter_array_chunks)]

for [a, b, c] in some_iter.array_chunks() {
    ...
}


And it handles remainders nicely:

#![feature(iter_array_chunks)]

for [a, b, c] in some_iter.by_ref().array_chunks() {
    ...
}

let rem = some_iter.into_remainder();


Performance

The iterator versions are particularly useful if you are not iterating over some contiguous collection. However, depending on your use-case you might find that collecting into a Vec first and using the slice methods might be faster even including the time allocate the iterator into a Vec. This is particularly true in the case of array_windows where the elements need to be cloned.

Code Snippets

use itermore::IterArrayChunks; // 0.7

fn main() {
    let some_iter = vec![1, 2, 3, 4, 5, 6].into_iter();

    for [prev, next] in some_iter.array_chunks() {
        println!("{}--{}", prev, next);
    }
}
1--2
3--4
5--6
Some([1, 2, 3, 4, 5])
Some([2, 3, 4, 5, 6])
None
#![feature(iter_array_chunks)]

for [a, b, c] in some_iter.array_chunks() {
    ...
}
#![feature(iter_array_chunks)]

for [a, b, c] in some_iter.by_ref().array_chunks() {
    ...
}

let rem = some_iter.into_remainder();

Context

Stack Overflow Q#42134874, score: 20

Revisions (0)

No revisions yet.