#243 Jul 6, 2026

243. slice::as_flattened — Treat a Slice of Arrays as One Flat Slice, No Copy

You’ve got a Vec<[f32; 3]> of RGB pixels and an API that wants &[f32]. The manual flatten allocates a whole second buffer. as_flattened hands you the same bytes as a flat &[f32] — zero copies, zero allocation.

The copy you didn’t need

A slice of fixed-size arrays is already contiguous in memory. But reach for a flat view the obvious way and you rebuild it element by element into a fresh Vec:

1
2
3
4
5
6
let pixels: Vec<[f32; 3]> = vec![[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]];

// Rebuilds every value into a new allocation
let flat: Vec<f32> = pixels.iter().flatten().copied().collect();

assert_eq!(flat, vec![1.0, 0.0, 0.0, 0.0, 1.0, 0.0]);

That collect walks all six values and heap-allocates a second buffer — pure waste when the layout you want is already sitting there.

as_flattened is a free reinterpretation

1
2
3
4
5
6
let pixels: Vec<[f32; 3]> = vec![[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]];

let flat: &[f32] = pixels.as_flattened();

assert_eq!(flat, &[1.0, 0.0, 0.0, 0.0, 1.0, 0.0]);
assert_eq!(flat.len(), 6); // outer_len * N

<[[T; N]]>::as_flattened takes &[[T; N]] and returns &[T] covering the exact same memory. No copy, no allocation — just a pointer and a length. The result borrows the original, so it stays as cheap as it looks.

Mutate through the flat view

as_flattened_mut gives you &mut [T], so you can run a flat transform over structured data without unpacking it:

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

for v in rows.as_flattened_mut() {
    *v *= 10;
}

assert_eq!(rows, [[10, 20, 30], [40, 50, 60]]);

Same storage, edited in place — the array grouping is still there when you’re done.

Where it shines: handing structured data to flat APIs

Vertex buffers, audio frames, matrices — anything you model as [T; N] but a lower-level API wants as one long run:

1
2
3
4
5
6
7
8
9
// A 2x3 matrix stored as rows
let matrix = [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]];

fn dot(a: &[f64], b: &[f64]) -> f64 {
    a.iter().zip(b).map(|(x, y)| x * y).sum()
}

let flat = matrix.as_flattened();
assert_eq!(dot(flat, flat), 91.0); // 1+4+9+16+25+36

You keep the readable [[f64; 3]; 2] shape in your own code and pass a &[f64] across the boundary — no glue buffer in between. Whenever you catch yourself flatten().collect()-ing a slice of arrays just to change its type, as_flattened is the zero-cost version.

← Previous 242. slice::binary_search_by_key — Find a Record by One Field, No Hand-Written Comparator Next → 244. clone_from — Reuse the Buffer You Already Have Instead of Reallocating