Destructuring the syntax of types in declarative macros

Hi,

I have a rust macro which takes a type in as one of its parameters. I am trying to use & before the typename as something of a flag, so that &MyType gets treated slighly differently by the macro's internals (via a second, helper macro) than MyType. The reason I decided to attempt that (at the risk of sounding XY) is that there doesn't seem to be any native way to support a "flag" with declarative macros, since repetitions must have a syntax variable in them in order to make use of any information about them (i.e., it is impossible to determine using $(&)? whether the & was actually present or not).

In this vein, I ran into an issue: the declarative macro engine seems to be incapable of separating the & from the rest of the type. I have produced a minimial working example here. Note that the result of running the code is no, even though the type passed into the macro does in fact begin with &. This seems counterintuitive to me, since I expected the syntax to just be dumped into the input of the second macro.

I have a multi-part question:

  1. Is this the intended/specified behaviour of the declarative macro system?
  2. Is there any known workaround or other method of incorporating a "flag" of sorts into a macro?

Note that I cannot simply have two patterns for my macro (one with a flag and one without), since the location of the "flag" in my case is nested within repetitions.

Try picking something other than &, since an ampersand can be part of a type when the type is a reference.

I understand that. The fact that & forms part of a valid type is necessary for this to work, since otherwise there is no way to pattern-match it and store it in a variable in the first place. For example, if I chose %, I could do $(%)? $t:ty, but I would have no way to determine whether or not the % was actually present; simply $t:ty would of course not match a type prefixed by %.

A few things:

Once you group a few tokens into a token type, you can't un-group them; & usize: $($token:tt)* can become &usize: $my_type:ty, but not the other way around.

As for your problem, a potential solution is to have two variants with/without the flag, (I'm assuming you're doing a tt muncher style macro), but to have a second macro do the switching and have a pretty interface:

macro_rules! pretty_foo {
    (@ $my_type:ty $($rest:tt)+) => {
        internal_foo((some internal param) $my_type);
        pretty_foo($($rest)+);
    };
    ($my_type: ty) => {
        internal_foo(() $my_type);
        pretty_foo($($rest)+);
    };
    () => {}
}
2 Likes

Thanks, that's the behaviour that I was not clear on.

That's not the case currently, but I may have to switch to that, or otherwise make the whole type + flag thing wrapped in some sort of brackets so it can show up as a single tt (at the cost of a less Rust-esque syntax).

Well, if your macro is sufficiently complex, it would probably be worthwhile porting it to a proc-macro over the course of a day or so.

1 Like