Const generics and compile-time computation expert covering const fn, type-level computation, const evaluation, MaybeUninit arrays, and compile-time validation.
// Generic over array size
struct Buffer<T, const N: usize> {
data: [T; N],
}
impl<T: Default + Copy, const N: usize> Buffer<T, N> {
fn new() -> Self {
Self {
data: [T::default(); N],
}
}
}
// Usage
let buf: Buffer<u8, 1024> = Buffer::new();
const fn fibonacci(n: u32) -> u64 {
match n {
0 => 0,
1 => 1,
_ => {
let mut a = 0;
let mut b = 1;
let mut i = 2;
while i <= n {
let tmp = a + b;
a = b;
b = tmp;
i += 1;
}
b
}
}
}
// Computed at compile time
const FIB_10: u64 = fibonacci(10);
// Also works in array sizes
const ARRAY: [u8; fibonacci(5) as usize] = [0; fibonacci(5) as usize];
use std::mem::MaybeUninit;
// Stack overflow risk for large arrays
fn bad_large_array() -> [u8; 1024 * 1024] {
[0; 1024 * 1024] // Stack overflow!
}
// โ
Good: Use heap
fn good_large_array() -> Box<[u8; 1024 * 1024]> {
Box::new([0; 1024 * 1024])
}
// โ
Good: MaybeUninit for uninitialized memory
fn uninit_array<const N: usize>() -> Box<[u8; N]> {
let mut data: Box<[MaybeUninit<u8>; N]> =
Box::new(unsafe { MaybeUninit::uninit().assume_init() });
for elem in &mut data[..] {
elem.write(0);
}
unsafe { Box::from_raw(Box::into_raw(data) as *mut [u8; N]) }
}
const fn validate_config(size: usize, alignment: usize) -> bool {
size > 0 && alignment.is_power_of_two()
}
struct Config<const SIZE: usize, const ALIGN: usize> {
_phantom: PhantomData<[u8; SIZE]>,
}
impl<const SIZE: usize, const ALIGN: usize> Config<SIZE, ALIGN> {
const fn new() -> Self {
assert!(validate_config(SIZE, ALIGN), "Invalid configuration");
Self { _phantom: PhantomData }
}
}
// Compile-time validation
const CONFIG: Config<1024, 8> = Config::new();
// const BAD: Config<0, 3> = Config::new(); // Compile error!
struct Uninitialized;
struct Initialized;
struct StateMachine<State, const N: usize> {
buffer: [u8; N],
_state: PhantomData<State>,
}
impl<const N: usize> StateMachine<Uninitialized, N> {
fn new() -> Self {
Self {
buffer: [0; N],
_state: PhantomData,
}
}
fn initialize(self) -> StateMachine<Initialized, N> {
StateMachine {
buffer: self.buffer,
_state: PhantomData,
}
}
}
impl<const N: usize> StateMachine<Initialized, N> {
fn process(&mut self) {
// Only available when initialized
}
}
const fn works() {
// โ
Arithmetic
let x = 1 + 2;
// โ
Conditionals
if x > 0 { }
// โ
Loops
let mut i = 0;
while i < 10 { i += 1; }
// โ
Match
match x {
0 => {},
_ => {},
}
// โ
Calling other const fn
const fn helper() -> i32 { 42 }
let y = helper();
}
const fn limitations() {
// โ Heap allocation (not yet stable)
// let v = Vec::new();
// โ Trait objects
// let obj: &dyn Trait = ...;
// โ Mutable references in const (limited)
// let mut x = 5;
// let r = &mut x;
// โ Floating point (improving)
// const F: f64 = 3.14;
}
Can be const if:
โ Value known at compile time
โ No heap allocation needed
โ No dynamic dispatch
โ Pure computation (no I/O)
Need:
โ Fixed-size array? Const generic
โ Compile-time computation? Const fn
โ Large array? MaybeUninit + Box
โ Validation? Const assertion
โ Type-level state? PhantomData + const generic
Const advantages:
โ
Zero runtime cost
โ
Compile-time validation
โ
Better optimization
โ
Smaller binary (sometimes)
Drawbacks:
โ Longer compile time
โ Limited feature set
โ Complex error messages
When using const:
# Check const evaluation
cargo build --release
cargo asm my_module::my_const_fn
# Verify array sizes
cargo check
# Test const assertions
cargo test --lib
Symptom: Segmentation fault
// โ Bad: large array on stack
let arr = [0u8; 1024 * 1024]; // Stack overflow!
// โ
Good: heap allocation
let arr = Box::new([0u8; 1024 * 1024]);
Symptom: Undefined behavior
// โ Bad: reading uninitialized
let mut arr: [u8; 100];
println!("{}", arr[0]); // UB!
// โ
Good: explicit initialization
let arr = [0u8; 100];
Symptom: Type mismatch errors
// โ Bad: mismatched sizes
fn process<const N: usize>(data: [u8; N]) {
let other: [u8; 10] = data; // Error if N != 10
}
// โ
Good: use generic consistently
fn process<const N: usize>(data: [u8; N]) -> [u8; N] {
data
}