Need a fixed-size array where each element depends on its index? Skip the vec![..].try_into().unwrap() dance — std::array::from_fn builds it in place with zero allocation.
The problem
Rust arrays [T; N] have a fixed size known at compile time, but initializing them with computed values used to be awkward:
1
2
3
4
5
6
| // Clunky: build a Vec, then convert
let squares: [u64; 5] = (0..5)
.map(|i| i * i)
.collect::<Vec<_>>()
.try_into()
.unwrap();
|
That allocates a Vec on the heap just to get a stack array. For Copy types you could use [0u64; 5] and a loop, but that doesn’t work for non-Copy types and is verbose either way.
The fix: array::from_fn
1
2
| let squares: [u64; 5] = std::array::from_fn(|i| (i as u64) * (i as u64));
assert_eq!(squares, [0, 1, 4, 9, 16]);
|
The closure receives the index (0..N) and returns the element. No heap allocation, no unwrapping — just a clean array on the stack.
Non-Copy types? No problem
from_fn shines when your elements don’t implement Copy:
1
2
3
| let labels: [String; 4] = std::array::from_fn(|i| format!("item_{i}"));
assert_eq!(labels[0], "item_0");
assert_eq!(labels[3], "item_3");
|
Try doing that with [String::new(); 4] — the compiler won’t let you because String isn’t Copy.
Stateful initialization
The closure can capture mutable state. Elements are produced left to right, index 0 first:
1
2
3
4
5
6
7
| let mut acc = 1u64;
let powers_of_two: [u64; 6] = std::array::from_fn(|_| {
let val = acc;
acc *= 2;
val
});
assert_eq!(powers_of_two, [1, 2, 4, 8, 16, 32]);
|
A practical example: lookup tables
Build a compile-time-friendly lookup table for ASCII case conversion offsets:
1
2
3
4
5
6
| let is_upper: [bool; 128] = std::array::from_fn(|i| {
(i as u8).is_ascii_uppercase()
});
assert!(is_upper[b'A' as usize]);
assert!(!is_upper[b'a' as usize]);
assert!(!is_upper[b'0' as usize]);
|
Why it matters
std::array::from_fn has been stable since Rust 1.63. It avoids heap allocation, works with any type (no Copy or Default bound), and keeps your code readable. Anytime you reach for Vec just to build a fixed-size array — stop, and use from_fn instead.