R
RUSTY YELLOW PAGES

#[recursion_limit = "N"] / #[type_length_limit = "N"]Attribute


Explanation

Both attributes are inner attributes placed at a crate root (#![recursion_limit = "N"], #![type_length_limit = "N"]) that raise a numeric limit the compiler otherwise enforces to keep certain kinds of unbounded recursion from spinning forever or blowing the stack during compilation. Neither has any effect on the compiled program's runtime behavior — both are purely compile-time ceilings.

#![recursion_limit = "N"] raises the limit on how deeply the compiler will recurse while expanding a recursive macro_rules! macro, or while resolving a deeply nested chain of trait bound implications. The default (128 at the time of writing, though the exact number can change between compiler versions) is generous enough for the overwhelming majority of code; hitting it produces an explicit compiler error naming recursion_limit and suggesting a specific new value. This tends to surface with macro_rules! macros implemented recursively over a variadic-like list of tokens (processing one item per recursive expansion) once that list gets long enough, or occasionally with very deep chains of generic trait bounds implying other trait bounds.

#![type_length_limit = "N"] raised a limit on how large the compiler would let a single monomorphized type's internal name grow — historically triggered by, for instance, deeply nested impl Trait return types or long iterator-adapter chains whose monomorphized type name grows with every .map()/.filter() link in the chain. This is largely a historical concern on modern compilers, which have grown considerably more tolerant of long type names on their own; it's now rarely the actual fix a compiler error suggests, though it can still appear in older code or with unusually extreme cases of type-name growth.

Be honest about both: these are escape hatches reached for only after hitting a specific compiler error that names the limit and suggests raising it — not something to add preemptively "just in case." Raising either limit doesn't fix a design problem; it just buys more room for something (often a macro that could be restructured iteratively instead of recursively) to keep going.

Usage examples

Raising the recursion limit for a token-counting macro

#![recursion_limit = "256"] // <- raises the macro-expansion/trait-resolution recursion ceiling

macro_rules! count_tokens {
    () => { 0 };
    ($_head:tt $($tail:tt)*) => { 1 + count_tokens!($($tail)*) };
}

const N: usize = count_tokens!(a b c d e f g h); // one recursive expansion per token

Designing a public API

A macro_rules! macro that recursively counts or processes a long variadic-style list of items hits the default recursion limit once enough items are passed at a call site — raising recursion_limit is the direct fix, reached for only after the compiler's own error names it.

#![recursion_limit = "512"] // <- raised only after hitting the default limit's compiler error

macro_rules! sum_of {
    () => { 0 };
    ($head:expr $(, $tail:expr)*) => { $head + sum_of!($($tail),*) };
}

fn total() -> i32 {
    // a long, generated call site with many arguments could exceed the default recursion depth
    sum_of!(1, 2, 3, 4, 5, 6, 7, 8)
}

The compiler's own error message for exceeding the default recursion limit names recursion_limit explicitly and suggests a concrete replacement value, which the Rust Reference documents as the intended way to respond — raising the crate-wide limit is appropriate once a specific macro's legitimate recursion depth is known to exceed the default, rather than guessing at a number in advance.

Explanation

Embedded support: Full

Both attributes are pure compile-time ceilings with no runtime footprint, so they apply identically under #![no_std]. If anything, #![recursion_limit] shows up more often in embedded code than in a typical hosted crate: peripheral-access crates generated from an SVD file (svd2rust output) or hand-written register-definition macros often expand a macro_rules! recursively once per register, or once per bit-field, across an entire chip family with hundreds of peripherals — and it's common for that recursion depth to exceed the default limit purely from the sheer number of registers being generated, not from anything algorithmically unusual. #![type_length_limit] isn't noticeably more or less relevant in embedded code than elsewhere; it stays a largely historical concern on modern compilers regardless of target.

Usage examples

Raising the recursion limit for a chip-family register-definition macro

#![recursion_limit = "512"] // <- raised after generating bindings for a chip with many peripherals hit the default

macro_rules! define_registers {
    () => {};
    ($name:ident: $addr:expr, $($rest:tt)*) => {
        pub const $name: u32 = $addr;
        define_registers!($($rest)*); // one recursive expansion per register in the list
    };
}

define_registers!(
    GPIOA_BASE: 0x4001_0800,
    GPIOB_BASE: 0x4001_0C00,
    GPIOC_BASE: 0x4001_1000,
    // ...continues for every peripheral on the chip
);