Tail recursive macros


#1

I’m trying to write a macro which features an optional repeated tail argument, but I can’t get it to work.

Here’s an example:

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

And the error is:

$head:expr is followed by a sequence repetition, which is not allowed for expr fragments

I found this example in a blog entry here https://danielkeep.github.io/quick-intro-to-macros.html, so I’m assuming this used to work. There’s also this stack overflow question: http://stackoverflow.com/questions/24512356/rust-how-to-fix-this-variadic-macro-rule.

Is this intentionally broken? If so, how is it recommended to write a recursive macro without explicitly writing a match for the the final single element. For example, I could write:

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

But in theory it seems like this should be unnecessary.


#3

Holy crap, someone read that shit. :smiley:

Yeah, that’s pretty badly out of date by now. Some really inconsiderate people went in and broke some of my favourite macro_rules tricks, including that one. Bastards.

Anyway, in this particular case, you have to do something like this, now:

#[macro_export]
macro_rules! count_exprs {
    () => { 0 };
    ($e:expr) => { 1 };
    ($e:expr, $($es:expr),+) => { 1 + count_exprs!($($es),*) };
}

Specifically, you have to put one of a small, fixed set of tokens after the $e:expr matcher. One of those is the comma.

Yes, it’s redundant and annoying, but sadly there’s no way around it and it is by design. Even more annoyingly, it’s for a good reason: if the set of things that can come after a given construct are unrestricted, then the Rust grammar almost cannot be extended without causing macro invocations to parse differently under new versions of the compiler. That said, this particular case is, I think, just a case of macro_rules! being a bit stupider than we’d like.


#4

Hm, aren’t macros unstable or something? I remember html5ever wanting to migrate to aster/quasi.


#5

Syntax extensions, written using Rust code, are unstable, but macro_rusles! macros are not. I think the main reason is that libsyntax is far from stable.


#6

Heh. You’re internet famous among people searching for “macro_rules recursion” :smile: I saw the RFC about future proofing the macros, but it wasn’t clear to me why a leading instead of a trailing comma would be an ambiguous parsing issue. It sounds like this is just one of those kinks that will eventually get worked out as the (very cool) Rust language matures.

I did eventually go with the additional single element base case, but it felt like I was missing something. Now that I know better, I’m perfectly content with accepting “that’s just the way it works”.