Compile-Time

#251 Jul 2026

251. include_str! — Ship the File Inside the Binary, Skip the Runtime Read

A missing template or SQL file in the deploy takes your app down at startup. include_str! bakes the file into the binary at compile time — a missing file becomes a compile error, not a production incident.

The runtime way — and its failure mode

1
2
3
// Runs at startup: I/O, error handling, and the
// file must be shipped alongside the executable.
let template = std::fs::read_to_string("greeting.txt")?;

This works until someone forgets to copy greeting.txt into the container, or the working directory isn’t what you assumed. The failure shows up at runtime, on someone else’s machine.

The compile-time way

1
2
// Embedded in the executable at compile time.
static TEMPLATE: &str = include_str!("greeting.txt");

The file’s contents become a &'static str inside your binary. No I/O, no Result, nothing to deploy alongside. If the file is missing or isn’t valid UTF-8, cargo build fails — the mistake never leaves your machine.

For binary assets there is include_bytes!, which gives you a &'static [u8]:

1
static LOGO: &[u8] = include_bytes!("logo.png");

Path gotcha

The path is relative to the current source file, not the crate root or working directory. For paths that survive refactoring into submodules, anchor them to the manifest:

1
2
3
static QUERY: &str = include_str!(
    concat!(env!("CARGO_MANIFEST_DIR"), "/queries/get_user.sql")
);

When to reach for it

SQL queries, HTML templates, license text, shader source, test fixtures — anything small and fixed at build time. Skip it for files that must be user-editable after deployment, or big enough to bloat the binary noticeably: embedding means every change requires a recompile. That’s the trade — and for config that should never drift from the code, it’s exactly what you want.

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.