Advanced Generics and Type-Level Programming
const fn
A const fn is a function or method marked with the const qualifier that can be called both in compile-time const contexts (such as const / static initializers, array lengths, and const { ... } blocks) and at runtime like an ordinary function.
Dual Execution Mode of const fn
Marking a function const fn does not force every call to run at compile time:
- When called inside a
constcontext (const X: u32 = compute(5);orlet x = const { compute(5) };), the compiler evaluates it at compile time. - When called in a runtime expression (
let x = compute(user_input);), it executes at runtime like a normalfn.
const fn fnv1a_hash(bytes: &[u8]) -> u64 {
let mut hash: u64 = 0xcbf2_9ce4_8422_2325;
let mut i = 0;
while i < bytes.len() {
hash ^= bytes[i] as u64;
hash = hash.wrapping_mul(0x0100_0000_01b3);
i += 1;
}
hash
}
// 1. Evaluated at compile time inside a `const` item:
const ROUTE_HEALTH_HASH: u64 = fnv1a_hash(b"/api/health");
fn main() {
// 2. Evaluated at runtime on dynamic input:
let dynamic_path = String::from("/api/health");
let runtime_hash = fnv1a_hash(dynamic_path.as_bytes());
println!("compile-time hash: {:#018x}", ROUTE_HEALTH_HASH);
println!("runtime hash: {:#018x}", runtime_hash);
println!("matches: {}", ROUTE_HEALTH_HASH == runtime_hash);
}
compile-time hash: 0x05288ce2d004bc35
runtime hash: 0x05288ce2d004bc35
matches: true
Capabilities Supported Inside const fn (Current Stable Rust / 2024 Edition)
Modern Rust supports almost all deterministic, allocation-free control flow inside const fn:
| Feature | Allowed in const fn? |
Notes |
|---|---|---|
| Arithmetic, bitwise, and logical operators | Yes | Checked for overflow at compile time |
if, if let, match, loop, while, while let |
Yes | for x in iter is not yet allowed because IntoIterator::into_iter is a trait method; use while i < slice.len() instead |
Local mutable variables and mutable references (&mut T) |
Yes | Full borrow checking inside const fn |
Calling other const fn functions and intrinsics |
Yes | Standard library integer, slice, pointer, and Option/Result constructors are const fn |
panic!("...") and assert!(...) |
Yes | Halts compilation with custom diagnostic message if reached during CTFE |
Floating-point arithmetic (f32, f64) |
Yes (stabilized in Rust 1.82) | Deterministic IEEE 754 operations |
&raw const / &raw mut and raw pointer dereferencing |
Yes | Validated by the CTFE Miri engine |
Heap allocations (Box::new, Vec::push, String::from) |
No | Dynamic heap allocations cannot yet escape into runtime memory |
Trait method calls (T::method()) |
No | Calling methods through generic trait bounds is not yet stable in const fn |
I/O, system calls, threads, or FFI (extern "C") |
No | Compile-time evaluation must be deterministic and sandboxed |
Why while Loops Replace for Loops in const fn
Because for item in slice desugars to calling the trait method IntoIterator::into_iter and Iterator::next—and trait methods are not yet const—iterating over a slice or array inside a const fn uses an index while loop or slice pattern matching (while let [first, rest @ ..] = slice):
const fn max_in_slice(mut slice: &[i32]) -> Option<i32> {
let [mut current_max, ref tail @ ..] = *slice else {
return None;
};
slice = tail;
while let [head, ref rest @ ..] = *slice {
if head > current_max {
current_max = head;
}
slice = rest;
}
Some(current_max)
}
const PEAK: Option<i32> = max_in_slice(&[14, 92, 37, 88, 5]);
fn main() {
println!("compile-time PEAK = {:?}", PEAK);
}
compile-time PEAK = Some(92)