Iterators

chunks() and Windows Through Standard-Library Alternatives

While the Iterator trait operates on one element at a time, contiguous slices (&[T] and &mut [T]) provide specialized iterators for fixed-size batching (.chunks(), .chunks_exact(), .as_chunks()) and overlapping sliding windows (.windows()).

Slice Chunking and Windowing Methods

Slice Method Step Size Remainder Behavior Yielded Item Type
slice.chunks(n) n (non-overlapping) Final slice has length 1..=n if len % n != 0 &[T]
slice.chunks_exact(n) n (non-overlapping) Guarantees every yielded slice has length n; remainder accessed via .remainder() &[T]
slice.as_chunks::<N>() N (const generic) Returns (&[[T; N]], &[T]) — fixed-size array references + remainder slice (&[[T; N]], &[T])
slice.rchunks(n) n (from the back) Final slice at the front has length 1..=n &[T]
slice.chunk_by(pred) Variable (runs) Splits wherever pred(&a, &b) returns false &[T]
slice.windows(n) 1 (overlapping) Yields 0 items if slice.len() < n &[T] (each of length n)
Slice: [ 10, 20, 30, 40, 50 ]

.chunks(2):         [10, 20],  [30, 40],  [50]       (non-overlapping)
.chunks_exact(2):   [10, 20],  [30, 40]              (remainder: [50])
.windows(3):        [10, 20, 30]
                        [20, 30, 40]
                            [30, 40, 50]             (sliding by 1)

Non-Overlapping Batches with .chunks() and .chunks_exact()

fn main() {
    let data = [1, 2, 3, 4, 5, 6, 7];

    let chunks: Vec<&[i32]> = data.chunks(3).collect();
    println!("chunks(3):       {chunks:?}");

    let mut exact = data.chunks_exact(3);
    println!("exact batch 1:   {:?}", exact.next().unwrap());
    println!("exact batch 2:   {:?}", exact.next().unwrap());
    println!("exact remainder: {:?}", exact.remainder());
}
chunks(3):       [[1, 2, 3], [4, 5, 6], [7]]
exact batch 1:   [1, 2, 3]
exact batch 2:   [4, 5, 6]
exact remainder: [7]

Overlapping Sliding Windows with .windows()

slice.windows(size) is ideal for computing moving averages, finite differences, or checking sorted adjacency across a slice:

fn main() {
    let prices = [100_i32, 105, 102, 110, 118];

    // Compute day-over-day price deltas using 2-element windows:
    let deltas: Vec<i32> = prices
        .windows(2)
        .map(|w| w[1] - w[0])
        .collect();

    // Group consecutive ascending runs using `chunk_by`:
    let ascending_runs: Vec<&[i32]> = prices
        .chunk_by(|a, b| a <= b)
        .collect();

    println!("Price deltas:   {deltas:?}");
    println!("Ascending runs: {ascending_runs:?}");
}
Price deltas:   [5, -3, 8, 8]
Ascending runs: [[100, 105], [102, 110, 118]]

Why Iterator Does Not Have a Zero-Allocation .windows() Method

slice::windows returns &[T] views directly into an already contiguous memory buffer in $O(1)$ time. A general Iterator produces values on the fly that are not stored contiguously in memory, so windowing an arbitrary Iterator requires either copying elements into a ring buffer or requiring Item: Clone.