How to make a macro that makes a macro that wraps format!

It is possible to make a macro that wraps format!:

macro_rules! my_format {
    ($($arg:tt)*) => {
        format!($($arg)*)
    }
}

Testing, this out with cargo expand, both format! and my_format! expand exactly the same.

Additionally, it's also possible to make macros that produce macros. The problem I when I create a macro that produces macros that wrap format!:

macro_rules! make_format {
    ($name:ident) => {
        macro_rules! $name {
            ($($arg:tt)*) => {
                format!($($arg)*)
            };
        }
    }
}

make_format!(my_format);

Theoretically, this code should do the exact same thing as the example above but in reality it produces the following compiler error:

error: attempted to repeat an expression containing no syntax variables matched as repeating at this depth
  --> src/main.rs:10:15
   |
10 |             ($($arg:tt)*) => {
   |               ^^^^^^^^^

How do I fix this?

You could use the nightly macro_metavar_expr to escape $:

For stable, I'm not sure if passing the dollar token explicitly will be desired.

In any case you could develop a proc macro.

1 Like

Thanks!

I did end up passing the dollar sign but using another macro to remove it from invocations:

macro_rules! with_dollar_sign {
    ($($body:tt)*) => {
        macro_rules! __with_dollar_sign { $($body)* }
        __with_dollar_sign!($);
    }
}

macro_rules! make_format {
    ($name:ident) => { 
        with_dollar_sign! {  
            ($d:tt) => {
                macro_rules! $name {
                    ($d($d arg:tt)*) => {
                        format!($d($d arg)*)
                    };
                }
            }
        }
    }
}

make_format!(my_format);

This (albeit an ugly solution) is better than using the macro_metavar_expr feature becase

  1. I would rather not use nightly Rust
  2. The macro I am working on will not be exported, so the namespace pollution doesn't matter too much

PS: Do you know if the with_dollar_sign macro can be refactored into an internal rule?

1 Like

You could use experimental declarative macros, but again nightly... and anyways how would the macro be accessed from the other macro, right?

You can at best use #[doc(hidden)].

1 Like

No I mean internal rules made with @:

#[macro_export]
macro_rules! foo {
    (@as_expr $e:expr) => {$e};

    ($($tts:tt)*) => {
        foo!(@as_expr $($tts)*)
    };
}

These can be used to eliminate helper macros and work in stable as part of declarative macros.

1 Like

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.