fmt-rfcs
fmt-rfcs copied to clipboard
Metavariables in macro def
Recently rustfmt started to format macro def.
Should rustfmt add a space after : of metavariables? Adding a space is consistent with other items like function or type declaration. However majority of the code in the wild seems to use the no-space style.
/// Add a space after `:`
macro_rules! foo {
($x: ident, $y: ident, $z: ident) => {};
}
/// No space after `:`
macro_rules! foo {
($x:ident, $y:ident, $z:ident) => {};
}
Also what should we do when metavariables are separated by separators other than ,, especially by :?
/// Add a space after `:`
macro_rules! foo {
($x: ident : $y: ident : $z: ident) => {};
}
/// No space after `:`
macro_rules! foo {
($x:ident : $y:ident : $z:ident) => {};
}
Metavariables were discussed back in the day in the style team - we agreed on $name:kind, because of examples like your second one.
Repeating what I said in https://github.com/rust-lang-nursery/rustfmt/issues/2534#issuecomment-375920084, macros should be formatted like normal code, i.e. with space in $name: kind and possibly an extra preceding space when : is used as a separator ($x: ident : $y: ident).
I can't find any discussion of macros in this repo and I don't recall coming to the conclusion in https://github.com/rust-lang-nursery/fmt-rfcs/issues/121#issuecomment-377454304, however, it is perfectly possible that we did - it was a long time ago. In the current guide, we don't have any discussion on macro defintions, and since they can be arbitrary tokens I'm not sure we even can give good guidance.
I do agree with @petrochenkov that we should make macros look like functions (since they are very similar in the common case). However, the no space pattern is common in the ecosystem and certainly looks better when :s are also used elsewhere.
@nrc
I do agree with @petrochenkov that we should make macros look like functions (since they are very similar in the common case).
There are a few interesting differences:
- Arguments to functions are comma-separated lists of patterns and types, while arguments to macros are just lists of tokens (separated by spaces only).
- Patterns and types in functions can be long and complicated (e.g.
mut foo: &'a Rc<RefCell<My<'b, String>>>), while token names and types in macros are always simply dollar-word-colon-word (e.g.$foo:ident).
It's important to note that in macros commas are tokens, too.
Consider this: $foo: tt, $bar: ident. There are actually three tokens at play here:
$foo: tt, $bar: ident
^^^^^^^^
^
^^^^^^^^^^^
I find it a little difficult to skim through this token list and visually figure out what is a token and what is a type. The following formatting is easier to follow for me:
$foo:tt , $bar:ident
Finally, separators are visually lightweight so I'm okay with gluing the comma to the first token:
$foo:tt, $bar:ident
Lately I've been working with macros a lot and found that I read function signatures and macro patterns differently:
-
In function signatures, I focus on types only. Patterns (variable names) are often less important. That's why in Haskell signatures like
length :: [a] -> Intread so well. -
In macro patterns it's the opposite. I focus on patterns only and ignore the token types. For example, when reading
[$i:expr] recv($r:expr, $m:pat) => $body:tt, $($tail:tt)*my mind just skips over the token types and focuses on the structure of the pattern and token names instead (iis index,ris receiver,mis message).