Data-Processing

50. slice::chunk_by — Group Consecutive Elements

Need to split a slice into groups of consecutive elements that share a property? chunk_by does exactly that — no allocations, no manual index tracking.

The problem

Imagine you have a sorted list of temperatures and want to group them into runs of non-decreasing values. Without chunk_by, you’d write a loop tracking where each group starts and ends:

1
2
let temps = [18, 20, 22, 19, 21, 25, 24];
// Manual grouping... indices, slicing, off-by-one bugs 😬

Enter chunk_by

Stabilized in Rust 1.77, slice::chunk_by splits a slice between consecutive elements where the predicate returns false. Each chunk is a sub-slice where every adjacent pair satisfies the predicate:

1
2
3
4
5
6
7
8
9
let temps = [18, 20, 22, 19, 21, 25, 24];

let runs: Vec<&[i32]> = temps.chunk_by(|a, b| a <= b).collect();

assert_eq!(runs, vec![
    &[18, 20, 22] as &[i32],
    &[19, 21, 25],
    &[24],
]);

The predicate |a, b| a <= b keeps elements in the same chunk as long as values are non-decreasing. The moment a value drops, a new chunk begins.

Group by equality

A common use case is grouping runs of equal elements:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
let data = [1, 1, 2, 3, 3, 3, 2, 2];

let groups: Vec<&[i32]> = data.chunk_by(|a, b| a == b).collect();

assert_eq!(groups, vec![
    &[1, 1] as &[i32],
    &[2],
    &[3, 3, 3],
    &[2, 2],
]);

Notice this groups consecutive equal elements — it’s not the same as a GROUP BY in SQL. The two runs of 2 stay separate because they aren’t adjacent.

Mutable chunks

There’s also chunk_by_mut if you need to modify elements within each group:

1
2
3
4
5
6
7
8
let mut data = [1, 1, 2, 3, 3, 3, 2, 2];

for chunk in data.chunk_by_mut(|a, b| a == b) {
    // Double the first element in each run
    chunk[0] *= 2;
}

assert_eq!(data, [2, 1, 4, 6, 3, 3, 4, 2]);

Key details

  • Zero-cost: returns sub-slices of the original data — no allocations
  • Predicate sees pairs: |a, b| receives each consecutive pair; a new chunk starts where it returns false
  • Works on any slice: &[T], &mut [T], Vec<T> (via deref)
  • Stable since Rust 1.77

Next time you reach for a manual loop to group consecutive elements, try chunk_by instead.