Macro expansion optionally in natural order

Nested macros are known as being evaluated “the wrong way round.” This makes it impossible to have a helper macro, the output of which you can recursively match against. To remedy this, I would suggest an additional backwards compatible syntax: If the outermost macro is called with a double exclam, then any nested macro calls that also have a double exclam would be expanded first. So

macro_rules! sum { () => { 1 + 2 } }

// unchanged from today:
assert_ne!("1 + 2 = 3", stringify!(sum!() = 3));

// new, 1st might give a warning as double exclam serves no purpose:
assert_ne!!("1 + 2 = 3", stringify!(sum!() = 3));
assert_ne!!("1 + 2 = 3", stringify!!(sum!() = 3));

// new, sometimes useful:
assert_eq!!("1 + 2 = 3", stringify!(sum!!() = 3));
assert_eq!!("1 + 2 = 3", stringify!!(sum!!() = 3));

Without requiring a double exclam as an activator on the outer call, it might break some macro that matches $id:ident!!(…). For such a hypothetical macro, this new syntax might not be useful. But that’s an edge case.

One place where this would be beneficial is for embedded languages, which you can’t easily keep in a separate file. For example, my upcoming RustDOT crate, which will be able to parse all Graphviz DOT (as long as it is lexically valid Rust, which it almost always is.) Instead of the first, you could directly do the second:

dot! {
    digraph "Hello World" {
        space [ label = " " ]
        World [ fontcolor = blue ]
        Hello -> "," -> space -> World -> "!"
    }
}
dot!!(include!!("hello-world.dot"))

NB: This is not the same as the similar suggestion for proc macros. That one, besides being for a different kind of macro, didn’t seem to offer finely granular choice, which inner macros to expand early.

Previous work: RFC: Eager Macro Expansion by pierzchalski · Pull Request #2320 · rust-lang/rfcs · GitHub which has been postponed.

If we do add a mechanism for eager macro expansion, I personally expect we'll want to do something less subtle than one versus two exclamation marks.

1 Like

Isn't there already a builtin macro which is eager in its arguments? IIRC, concat!(include_str!("filename.txt")) doesn't work. How about a now! or eager! macro that does this? Though maybe that is an instance of this problem as well (it's late, but I don't want to let this thought slip away overnight)…concat! not "waiting" for inner expansion to act. And I suppose any now!-like macro runs into the case of something trying to match that literally…

1 Like

The general thinking nowadays is to use macro metavariable expressions-like syntax.

1 Like

We have the proc-macro-expand feature to allow proc-macros to do the same thing as builtins.

That's coming from the other direction though, the outer macro knows that this specific part of its input should be allowed to expand, rather than the caller of the macro forcing it to pre-expand before the outer macro runs.

2 Likes