#238 Jul 3, 2026

238. slice::chunks_exact_mut — Edit a Slice in Fixed-Size Blocks Without Index Math

Processing a buffer N elements at a time usually means a while i + N <= len loop and a pile of buf[i..i+N] slicing. chunks_exact_mut hands you each fixed-size block as a &mut [T] — no index bookkeeping, no off-by-one.

The manual-window loop

1
2
3
4
5
6
7
let mut buf = [10, 20, 30, 40, 50, 60];
let mut i = 0;
while i + 2 <= buf.len() {
    buf[i..i + 2].reverse(); // swap each adjacent pair
    i += 2;
}
assert_eq!(buf, [20, 10, 40, 30, 60, 50]);

It works, but every one of i + 2, <= len, and i += 2 is a place to get the bounds wrong.

chunks_exact_mut yields the blocks for you

1
2
3
4
5
let mut buf = [10, 20, 30, 40, 50, 60];
for pair in buf.chunks_exact_mut(2) {
    pair.reverse();
}
assert_eq!(buf, [20, 10, 40, 30, 60, 50]);

Each pair is a &mut [T; 2]-shaped slice you can mutate in place. The iterator stops once fewer than 2 elements remain, so you never index past the end.

The “exact” part: a leftover tail is skipped, not sliced

Unlike chunks_mut, the last partial block is not yielded — that guarantee is exactly why the block size is reliable:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
let mut buf = [1, 2, 3, 4, 5]; // 5 isn't a multiple of 2
let mut it = buf.chunks_exact_mut(2);
for pair in it.by_ref() {
    pair.swap(0, 1);
}
// grab the odd element that didn't fill a block
let tail = it.into_remainder();
assert_eq!(tail, &mut [5][..]);

assert_eq!(buf, [2, 1, 4, 3, 5]); // last 5 left untouched

Reach for the remainder through by_ref() + into_remainder(): iterate the full blocks, then claim whatever fell short. If you drop the loop’s by_ref(), the iterator is moved and into_remainder is unavailable.

Why not just chunks_mut?

chunks_mut(n) also walks a slice in steps of n, but its final chunk can be shorter than n, so any code assuming a fixed width needs a length check every iteration. chunks_exact_mut trades that partial tail for a compile-time-friendly promise that every yielded block is exactly n long — which also lets the optimizer generate tighter code. There’s a read-only chunks_exact for &[T], and as_chunks_mut if you want real &mut [T; N] arrays instead of slices.

← Previous 237. Iterator::eq — Compare Two Sequences Without Collecting Them First Next → 239. Vec::drain — Remove a Range and Keep What You Pulled Out