Advanced Generics and Type-Level Programming

Const Parameters

Const parameters (const IDENT: Type) are the formal declarations of compile-time constant arguments in generic parameter lists (<const N: usize>). Stable Rust restricts which types may be used as const parameter types so the compiler can compare two instantiated types for structural equality (Type<A> == Type<B>) without ambiguity.

Allowed Types for const Parameters

In stable Rust, a const generic parameter must have one of the following structural-match primitive types:

Allowed Type Category Concrete Types Example
Unsigned integers u8, u16, u32, u64, u128, usize struct Buffer<const CAP: usize>
Signed integers i8, i16, i32, i64, i128, isize struct FixedPoint<const SCALE: i32>
Booleans bool struct Channel<const BOUNDED: bool>
Unicode scalar values char struct Delimited<const SEP: char>

Floating-point types (f32, f64, because NaN != NaN and -0.0 == +0.0 have ambiguous type-equality semantics), string slices (&'static str), and user-defined structs/enums are rejected as const parameter types on stable Rust (E0741):

// ❌ does not compile
struct NamedMetric<const NAME: &'static str> {
    value: u64,
}

fn main() {}
error[E0741]: `&'static str` is forbidden as the type of a const generic parameter
 --> src/main.rs:2:32
  |
2 | struct NamedMetric<const NAME: &'static str> {
  |                                ^^^^^^^^^^^^
  |
  = note: the only supported types are integers, `bool`, and `char`

Default Values for const Parameters

Like generic type parameters, const parameters on struct, enum, type alias, and trait definitions can declare default values (const N: usize = 64):

#[derive(Debug)]
struct SmallBuffer<T, const INLINE_CAP: usize = 8> {
    storage: [Option<T>; INLINE_CAP],
}

impl<T, const INLINE_CAP: usize> SmallBuffer<T, INLINE_CAP> {
    fn new() -> Self {
        Self {
            storage: [const { None }; INLINE_CAP],
        }
    }

    fn capacity(&self) -> usize {
        INLINE_CAP
    }
}

fn main() {
    // Uses default `INLINE_CAP = 8`:
    let default_buf: SmallBuffer<u32> = SmallBuffer::new();
    // Overrides `INLINE_CAP = 16`:
    let custom_buf: SmallBuffer<u32, 16> = SmallBuffer::new();

    println!("default capacity: {}", default_buf.capacity());
    println!("custom capacity:  {}", custom_buf.capacity());
}
default capacity: 8
custom capacity:  16

Passing Expressions as Const Arguments ({ EXPR })

When supplying a const generic argument at an instantiation site:

  • Single literals (16, true, ':') and standalone const / parameter identifiers (N, MAX_LEN) can be written directly: Buffer::<u8, 16>, Buffer::<u8, MAX_LEN>.
  • Compound constant expressions (2 * 1024, size_of::<u64>(), 1 << 4) must be enclosed in curly braces { ... }: Buffer::<u8, { 2 * 1024 }>:
fn zero_bytes<const BYTES: usize>() -> [u8; BYTES] {
    [0_u8; BYTES]
}

fn main() {
    const HEADER_LEN: usize = 4;
    // Compound expression `{ HEADER_LEN + size_of::<u64>() }` wrapped in `{ ... }`:
    let packet = zero_bytes::<{ HEADER_LEN + size_of::<u64>() }>();

    println!("packet length: {} bytes", packet.len());
}
packet length: 12 bytes