Simple Macro with Recursion: Recursion limit hit directly

I've been following the book: Push-down Accumulation.

One macro in the book is defined as:

macro_rules! init_array {
    (@accum (0, $_e:expr) -> ($($body:tt)*))
        => {init_array!(@as_expr [$($body)*])};
    (@accum (1, $e:expr) -> ($($body:tt)*))
        => {init_array!(@accum (0, $e) -> ($($body)* $e,))};
    (@accum (2, $e:expr) -> ($($body:tt)*))
        => {init_array!(@accum (1, $e) -> ($($body)* $e,))};
    (@accum (3, $e:expr) -> ($($body:tt)*))
        => {init_array!(@accum (2, $e) -> ($($body)* $e,))};
    (@as_expr $e:expr) => {$e};
    [$e:expr; $n:tt] => {
        {
            let e = $e;
            init_array!(@accum ($n, e.clone()) -> ())
        }
    };
}

What I don't understand is why the macro cannot be defined as recursion. I tested writing it recursion style but hit the recursion limit directly (which in my opinion shouldn't be so) with the following code let array = init_array![String::from("Hello"); 3].

Here is the recursion example:

macro_rules! init_array {
    (@accum (0, $_e:expr) -> ($($body:tt)*)) => {
        init_array!(@as_expr [$($body)*])
    };
    (@accum ($n:expr, $e:expr) -> ($($body:tt)*)) => {
        {
            let _n = $n - 1;
            init_array!(@accum (_n, $e) -> ($($body)* $e,))
        }
    };
    (@as_expr $e:expr) => {$e};
    [$e:expr; $n:tt] => {
        {
            let e = $e;
            init_array!(@accum ($n, e) -> ())
        }
    };
}

Why is this?

_n - 1 isn't folded and never matches 0, only 3 - 1 then 3 - 1 - 1 etc..

Even if it would, the moment you captured $n as expr, it cannot be decomposed anymore and will never match 0.

When forwarding a matched fragment to another macro-by-example, matchers in the second macro will see an opaque AST of the fragment type. The second macro can't use literal tokens to match the fragments in the matcher, only a fragment specifier of the same type. The ident , lifetime , and tt fragment types are an exception, and can be matched by literal tokens.

The reference.

So you always hit the second arm and never the first arm, an infinite recursion.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.