Macros

Compile-Time Code Generation

Rust provides three distinct mechanisms for compile-time evaluation and code generation: const fn / const evaluation, declarative and procedural macros, and build.rs build scripts (combined with include! or include_str!).

Comparing the Three Compile-Time Mechanisms

Mechanism Operates On Can Generate New Types / Functions / Impls? Can Read External Files / Schemas? Type-Checked Definition?
const fn & const { ... } Typed values (u32, [T; N], &str) No (computes values only) No (except via include_bytes! / include_str!) Yes (checked when defined)
Macros (macro_rules!, proc-macro) Syntax tokens (TokenStream) Yes (struct, enum, impl, fn) No (sandboxed to input tokens) Checked at each expansion site
Build Scripts (build.rs + include!) Filesystem & environment Yes (writes .rs file into $OUT_DIR) Yes (.proto, .sql, C headers) Checked when included

Combining macro_rules! with const Evaluation

When generating lookup tables, bitmasks, or dispatch tables at compile time, use macros to generate the structural boilerplate (enum, match, FromStr, Display) and const expressions to validate invariants or precompute arrays at compile time:

macro_rules! define_http_statuses {
    ( $( $variant:ident = $code:literal => $reason:literal ),+ $(,)? ) => {
        #[derive(Debug, Clone, Copy, PartialEq, Eq)]
        #[repr(u16)]
        pub enum HttpStatus {
            $( $variant = $code, )+
        }

        impl HttpStatus {
            pub const ALL: &'static [HttpStatus] = &[
                $( HttpStatus::$variant, )+
            ];

            pub const fn code(self) -> u16 {
                self as u16
            }

            pub const fn reason(self) -> &'static str {
                match self {
                    $( HttpStatus::$variant => $reason, )+
                }
            }

            pub const fn from_code(code: u16) -> Option<Self> {
                match code {
                    $( $code => Some(HttpStatus::$variant), )+
                    _ => None,
                }
            }
        }
    };
}

define_http_statuses! {
    Ok = 200 => "OK",
    Created = 201 => "Created",
    NotFound = 404 => "Not Found",
    InternalError = 500 => "Internal Server Error",
}

fn main() {
    // Computed at compile time via `const fn`:
    const STATUS: Option<HttpStatus> = HttpStatus::from_code(404);
    let s = STATUS.unwrap();

    println!("{} {} (total defined = {})", s.code(), s.reason(), HttpStatus::ALL.len());
}
404 Not Found (total defined = 4)

Notice how a single source of truth inside define_http_statuses! { ... } generates the #[repr(u16)] enum, the ALL slice, code(), reason(), and from_code()—making it impossible for a variant's numeric code and string reason to get out of sync!