Rust macro inside another rust macro is not getting run

Code: https://codeberg.org/vellu/rimpiazza/src/branch/main/src/lib.rs

Example:

fn main() {
    println!(
        "{}",
        replace!("I like Gelato", "Gelato" => "ice cream, a lot!", "like" => "hate")
    );
}

However, my problem is if I do this:

fn main() {
    println!(
        "{}",
        replace!(include_str!("./file"), "Gelato" => "ice cream, a lot!", "like" => "hate")
    );
}

This will not replace anything, it will try to replace include_str!(...) and not the actual ./file contents

Macro evaluation order is not, in general, reliable. It's also somewhat chaotic currently; some std macros are special-cased in the compiler according to how you want them to expand most of the time.

If you want to operate on a file, you should probably open the file right from your proc-macro instead.

1 Like

Unfortunate

Yep, this is one of the most important annoyances of the language currently. Unfortunately, it's a hard problem to solve, due to the very nature and purpose of macros.

Since macros are for transforming code, there isn't a single, unambiguously "best" evaluation order to apply, pretty much by definition:

  • Sometimes, you want to transform code that contains macros, and you want to preserve those inner macros, leaving them to be expanded after the transformation. (Or maybe you just want to detect that they are there.) In these cases, the compiler should go "from the outside in", evaluating outer macros first.
  • However, at other times, you want to evaluate inner macros first – that's what you need here. In these cases, the compiler should go "from the inside out", evaluating macros called deeper first, and feeding their expansion result to outer macros.
  • Worse yet, you might want a mix of the two. Evaluate some inner macros first, but not others, continue expansion at some outer level, then go back some more levels deeper.

I can imagine that giving control to the user over all of this is quite the feature to design. I don't know a good solution to this problem off the top of my head. I can assure you that the language team has spent a non-trivial amount of time (and effort) designing the language, but unfortunately, there are challenges that are just hard to tackle.

3 Likes

What about adding some kind of operator?
For example, add!!() with two !s will be included after the macro, and with just one ! it can be parsed through

The problem is that macros can be arbitrarily deeply nested. If you write something like this:

foo!{
    bar!!{
        qux!{
            wut!!(1234);
        }
    }
}

then what should the expansion order be? It could either be:

  • evaluate wut!!, substitute its expansion into the call to qux!, evaluate bar!! with that whole thing as its input, because wut!! asks to be eagerly expanded; or
  • evaluate bar!! with its input as the verbatim token tree as it appears in the source, without evaluating qux!, because it asks not to be eagerly expanded.

(And if you have more levels and more macro invocations at each level, the number of possibilities blows up exponentially.)

You could that the more !s you write, the later in the queue is gonna be parsed :sweat_smile:

@H2CO3 isn't this what i'm looking for? TokenStream in proc_macro - Rust

That sounds pretty limited (and also nightly-only). But yes, one day it will probably be useful.

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.