Would this syntax be possible with macro_rules?

I'd like to write this:

macro_rules! my_macro {
    $my_ident1:ident : $my_type1:ty => $my_type2:ty =
      struct {
        $($member:ident : $type:ty = $initial_v:expr),*
      } => { ... };
    $my_ident1:ident : $my_type1:ty =
      struct {
        $($member:ident : $type:ty = $initial_v:expr),*
      } => { ... };

More or less like this:

macro_rules! my_macro
where $my_id1:ident, $my_id2:ident, $member:ident,
      $my_ty1:ty, $my_ty2:ty, $ty:ty, $initial_v:expr,
    $my_id1 : $my_ty1 => $my_ty2 =
      struct { $($member : $ty = $initial_v),* } => { /* ... */ };
    $my_id1 : $my_ty1 =
      struct { $($member : $ty = $initial_v),* } => { /* ... */ };

Short answer: no.

Long answer: no, it violates the syntax rules for macros_rules.

Couldn't it be added via some other macro, or maybe added (in some form) to the core language?

It might be possible with procedural macros.

This is absolutely not possible with procedural macros. As proposed, this would require existing macro_rules! to be burned to the ground.

Personally, I think this is a bad idea... but, if you're going to argue for this, a few things you might want to consider:

  • You'll have more luck if you're not also simultaneously suggesting the basic syntax of macro_rules be altered, breaking all backward compatibility. This will not happen, and will almost certainly sink your efforts.

Edit: hit post, close tab, brain wakes up. Actually, if you ignore the whole "change what a macro even looks like" issue, you could do this without breaking back compat by replacing macro_rules! with an incompatible macro you explicitly import. I can't imagine something like that being added to the standard library (let alone by default), and really you'd have more luck proposing a different construct that's not called macro_rules!, but that then raises the question of: "why not just add this to macro_rules! in a backward-compatible fashion instead?"

  • If you move the where inside the outer braces, you save yourself from also needing to convince people to change the fundamental structure of macro invocations. Stick to one huge change at a time, not three.

  • You need to address the new syntax being ambiguous: is $my_id1:path in a pattern overriding the outer binding, or literally matching an ident followed by : and path?

  • Show an actual, compelling use-case: a real macro that shows significant benefits from this. If you want this, you need to convince people that they want it too.

  • Other points: if macros behave like this, why shouldn't function arguments behave this way, too? Do other languages do this? If C previously did exactly this, then decide it was a terrible idea and move away from it, why is it now a good idea?

1 Like

Thanks for the suggestions. As far as the name or the exact syntax go, I don't mind alternatives, as long as it can still do what I care about: readable macro patterns that aren't peppered with fragment types.

  • If you move the where inside the outer braces, you save yourself from also needing to convince people to change the fundamental structure of macro invocations. Stick to one huge change at a time, not three .

Wouldn't putting it inside the braces change the structure just as much?

  • You need to address the new syntax being ambiguous: is $my_id1:path in a pattern overriding the outer binding, or literally matching an ident followed by : and path ?

The way I thought about it, such override functionality would be useless (just add another $fragment if you need an additional type), and the presence of the where clause would literally make your given snippet match an ident followed by : and path.

Show an actual, compelling use-case: a real macro that shows significant benefits from this. If you want this, you need to convince people that they want it too .

True, but I'm not sure I'm quite there yet. I'd rather first make sure I'm not wasting time pushing an incomplete design. For now, I'm happy exchanging ideas with those who 'just get it'.

  • Other points: if macros behave like this, why shouldn’t function arguments behave this way, too? Do other languages do this? If C previously did exactly this, then decide it was a terrible idea and move away from it, why is it now a good idea?

Are you referring to the pre-ANSI C function declarations? If so, my answer would be that function args are simple lists of identifier-type pairs, whereas macro patterns can express more complex structures that are not as easy to visually parse. It would also allow shared identifiers across different patterns, making shared structures to be more obvious.

That being said, I find the C situation to be quite different, in that it was simply a matter of moving a bit of syntax inside the () as far as I can tell.

decide it was a terrible idea

I'm not sure what made the pre-ANSI syntax so terrible.

No. Macro invocations are of the form $name:ident ! $tail:group, or $name:ident ! $arg:ident $tail:group (where group is not a real kind, but if it existed it would match ( ... ), [ ... ], or { ... }).

Rust literally does not care what's inside $tail, only that it looks like a group. Putting stuff outside of that requires that you redefine what a macro invocation looks like. Parsing the contents of the group is the macro's job, not Rust's.

They're not, but throw in a bit of whitespace and it's rarely an issue. Having to constantly scroll up and down a potentially long macro definition to find out what each capture is being parsed as would make a complex macro harder to understand, though.

I only proposed a change to macro definitions though, not invocations. My change would semantically be exactly equivalent to the already-legal macro_rules snippet I posted in my OP above.

Same thing goes for functions though - you have to scroll to the top of the function to see the types of the arguments. However, I'd be alright with moving the where clause inside each branch in some form or another, to mitigate the scrolling problem, if preferrable.

macro_rules! is a macro. Defining a macro using macro_rules! is a macro invocation.

Also, your initial example is not valid. If you don't realise that, then I think you seriously need to improve your understanding of the macro system before proceeding.

Ah, I see what you mean now. I'll take that into consideration.

It's a trimmed-down version of something that is, but I didn't bother checking it with a compiler a second time, sorry about that.

Well I tagged my topic with the "help" tag for a reason. I don't think anything I said should be taken as coming from an expert on the subject. That said, your tone here feels somewhat threatening. I apologize if anything I said came across as offensive.

If you're suggesting syntax changes, you need to make sure your syntax is (outside of the changes themselves) correct.

Yes, but then it starting morphing into a request for changes (with an associated post to the internals forum). That makes it hard to work out exactly where you're coming from on this.

It also doesn't help that you directly made this about syntax, not semantics. Again, if you're going to explicitly talk about syntax, and propose changes to it, it's not unreasonable to expect you to know what the existing syntax is, and how it fits together.

It's not a threat, and I'm sorry that it came across that way. You are not offending me (or likely anyone). It's just frustrating to see an idea, which might be a valuable addition to the language, being presented poorly.

You're proposing changes to parts of the language that you're coming across as not understanding. You cannot possibly hope to properly articulate your ideas, nor defend them, if you don't understand how the existing system works. If your idea is a good idea, then it deserves better than that.

I said I thought you needed to better understand the macro system because doing so will dramatically improve your ability to argue for this change. Both in terms of knowing how to formulate the change itself (better bet would be on putting the where clause inside the outer braces before the first rule), and in being able to prepare counter-arguments to criticism (examples showing the benefits, explicitly defining semantics, etc.).

Indeed, and it's embarrasing, thank you very much for twisting the knife :blush:

I thought it'd be nice to have something that helps with the verbosity of the macro patterns, initially hoping there's already a way to do it in the current version of Rust, and I care very little where the where clause would go, whether it would really be in the form of a where clause, whether it would be part of macro_rules or part of some other macro, whether it would be procedural or not, whether it would be part of the core language, standard library or an external crate. All of those options suit me just fine and I'd be more interested in getting the feature than holding a torch for a particular bikeshed.

I posted this in both forums, figuring it's relevant to both places, and if people don't find the extra syntax to be a good thing, no harm no foul (or so I thought).

Indeed, "offend" was a poor word choice on my part, but I'm glad I brought the matter up either way.

You called it a bad idea 4 hours ago, so now it's me who's not sure where you're coming from.

As for my understanding of the subject, fair enough, but I'm afraid I'm not sure what the agreeable way to improve on the subject is if discussions like this one aren't fair game.

Do keep in mind the rapidity of this discussion, and that it occurred during early morning hours in the States, where many Rust developers live. I've found the experienced, long-time members of this forum to be exceptionally kind and generous. That includes @DanielKeep, who joined the forum over 4 years ago and has responded to queries many, many times since.

To emphasise: there's been no foul on your part.

I brought it up because the internals forum is not really where you go to ask questions about the language, it's where you go to change it. When I saw that, my perception of the thread shifted more into "they're seriously proposing this" territory.

Oh, I do think it's a bad idea. Putting the issues with the syntax you showed aside, I think separating the patterns into disjoint parts is just going to make complex ones harder to understand.

But that's entirely unrelated to the issue of how you're presenting it, and the above is just, like, my opinion. Keeping in mind that this is absolutely not a fight: I don't want you to win, but I don't want to see you go into the ring with both hands tied behind your back. That's not fair.

It is (and should be) fair game. But as I said, I think you should understand a system as best you can before proposing changes.

Insofar as being able to sidestep the "syntax" issue, this construction should be entirely backward-compatible:

macro_rules! my_macro {
    where ($my_id1:ident, $my_id2:ident, $member:ident,
      $my_ty1:ty, $my_ty2:ty, $ty:ty, $initial_v:expr);
    ($my_id1 : $my_ty1 => $my_ty2 =
      struct { $($member : $ty = $initial_v),* }) => { /* ... */ };
    ($my_id1 : $my_ty1 =
      struct { $($member : $ty = $initial_v),* }) => { /* ... */ };

At which point, this is no longer a question of syntax, but of semantics. As a bonus, that construction should be doable with procedural macros, which would allow you to do a proof of concept. Plus, if you can show people actively using it, that should greatly improve your argument.

Really, that's all I was trying to do with my initial post in this thread; point out what I saw as serious weaknesses in what it appeared you were seriously proposing. I suppose my mistake was in taking your proposal at face value when you said "syntax". Then again, I've seen people suggest even more disruptive changes before, so it's hard to tell sometimes.

Either way, I apologise if I've bogged this thread down. I do hope this change doesn't happen... but I also hope that if you do want to pursue it that you give it the best possible shot. :stuck_out_tongue:

... although, I suppose I should probably mention that there are plans afoot to completely replace macro_rules! with a new system anyway, and you should probably focus your attention on that. Not sure what the status on that is at present, though.

I'm honestly not 100% sure what you're trying to say here, so forgive me if I'm off base.

To be fair, if they're new, they don't know that. And they really shouldn't have to. If they've got the wrong impression because I was insufficiently clear, I should clear that up. Better that they voice this concern than not. I do very much appreciate it being a concern rather than an accusation.

As for the last sentence: I really don't like appeals to... well, not authority in my case. Hanging around like a particularly gharish set of curtains? Either way, how long I've been here is irrelevant, as is my post history; either my arguments are valid and/or I've accurately communicated them, or they aren't and/or I haven't.

Again, to be clear: I believe you're just reassuring the OP. That last sentence just made me feel like I had to say something.

1 Like

I posted it there because I thought it had to do with macro_rules' internals, figured an "internals" perspective would help and hoped that posting it there wouldn't necessarily be construed as a proposal. I purposely avoided referring to it as an "RFC" to avoid this. Posting in the internals forum might not have been "a foul", but it was most definitely counterproductive from where I'm standing.

I'll keep that in mind from now on. I thought it's a place to discuss internals generally, while the RFCs repo is the place to kickstart actual changes.

A procedural macro is good enough for me. I doubt I'll care enough about it becoming anything more than that as long as it works, so I'll just try implementing one and stop arguing for anything else.

Not at all, if placing the where inside the {} really turns out to be doable with procedural macros, I'll find this talk worth it.

Heard of it, but figured it's more expedient to work with what I already have.

I think what gave me the wrong impression the most is the purpose of the internals forum. To be frank, all I want is a programming language that works for me, and I would ideally find all I need from it to already be present. I don't care about spending time figuring out the purpose of each Rust forum, github repo, IRC channel, subreddit etc., composing RFCs, evangelizing features etc. I have little interest in becoming a language contributor or active member of the community in any significant capacity. My next step is most likely to try and implement the syntax as per your suggestion for my own purposes and that'll be the end of it.

Would replying a day later be of any help?

This is getting awfully meta.

More on topic, the spirit of the suggestion is something I can definitely sympathize with. It can be very jarring (especially in more complicated macro_rules! macros) that the left hand side requires matcher types while the right hand side requires not having them, and all too often do I copy and paste something from one side into the other and end up with a broken macro. To me, this is a problem worth solving in some manner.

1 Like