Patterns

71. Inline const Blocks — Compile-Time Evaluation Anywhere

Need a compile-time value in the middle of runtime code? Wrap it in const { } and the compiler evaluates it on the spot — no separate const item needed.

The old way

When you needed a compile-time constant inside a function, you had to hoist it into a separate const item:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
fn describe_limit() -> &'static str {
    const MAX: usize = 2_usize.pow(16);
    if MAX > 50_000 {
        "high"
    } else {
        "low"
    }
}

fn main() {
    assert_eq!(describe_limit(), "high");
}

It works, but the const declaration is noisy — especially when you only use the value once and it clutters the function body.

Enter const { }

Since Rust 1.79, you can write const { expr } anywhere an expression is expected. The compiler evaluates it at compile time and inlines the result:

1
2
3
4
fn main() {
    let limit = const { 2_usize.pow(16) };
    assert_eq!(limit, 65_536);
}

No named constant, no separate item — just an inline compile-time expression right where you need it.

Generic compile-time values

const { } really shines inside generic functions, where it can compute values based on type parameters:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
fn make_mask<const N: usize>() -> u128 {
    const { assert!(N <= 128, "mask too wide") };
    if N == 128 { u128::MAX } else { (1u128 << N) - 1 }
}

fn main() {
    assert_eq!(make_mask::<8>(), 0xFF);
    assert_eq!(make_mask::<16>(), 0xFFFF);
    assert_eq!(make_mask::<1>(), 1);
}

The const { assert!(...) } fires at compile time for each monomorphization — if someone writes make_mask::<200>(), they get a compile error, not a runtime panic.

Compile-time assertions

Use const { } to embed compile-time checks directly in your code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
fn process_buffer<const N: usize>(buf: [u8; N]) -> u8 {
    const { assert!(N <= 1024, "buffer too large") };
    buf[0]
}

fn main() {
    let small = process_buffer([1, 2, 3]);
    assert_eq!(small, 1);

    // This would fail at compile time:
    // let huge = process_buffer([0u8; 2048]);
}

The assertion runs at compile time — if it fails, you get a compile error, not a runtime panic. It’s like a lightweight static_assert from C++, but it works anywhere.

Building lookup tables

const { } shines when you need a precomputed table without polluting the outer scope:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
fn is_vowel(c: char) -> bool {
    const { ['a', 'e', 'i', 'o', 'u'] }.contains(&c)
}

fn main() {
    assert!(is_vowel('a'));
    assert!(is_vowel('u'));
    assert!(!is_vowel('b'));
    assert!(!is_vowel('z'));
}

The array is built at compile time and the contains check runs at runtime — clean, fast, and self-contained.

Next time you’re about to write const TEMP: ... = ...; just to use it once, reach for const { } instead. It keeps the value where it belongs — right at the point of use.