diff --git a/src/macros-by-example.md b/src/macros-by-example.md index 2c49300cd..8dbb88134 100644 --- a/src/macros-by-example.md +++ b/src/macros-by-example.md @@ -193,6 +193,76 @@ compiler knows how to expand them properly: not have the same number. This requirement applies to every layer of nested repetitions. +## Metavariable expressions + +Metavariable expressions in declarative macros provide expansions for information about metavariables that are otherwise not easily obtainable. + +| Expression | Meaning | +|----------------------------|------------| +| `${ignore(ident)}` | Binds `$ident` for repetition, but expands to nothing. | +| `$$` | Expands to a single `$`, for removing ambiguity in recursive macro definitions. | + +### Ignore + +`${ignore(ident)}` repeats an expansion the same number of times a metavariable repeats without actually expanding the metavariable. + +```rust +macro_rules! count { + ( $( $ignored_identifier:ident ),* ) => {{ + 0 $( + 1 ${ignore(ignored_identifier)} )* + }}; +} + +fn main() { + assert_eq!(count!(T, T, T), 3); +} +``` + +The above snippet will expand to a sequence of numbers respecting the number of times that `ignored_identifier` repeats, resulting in a final `0 + 1 + 1 + 1` output. + +### Dollar-dollar ($$) + +`$$` expands to a single `$`. + +Since metavariable expressions always apply during the expansion of a macro, they cannot be used in recursive macro definitions and this is where `$$` expressions comes into play, i.e., `$$` can se used to resolve ambiguities in nested macros. + +```rust +#![feature(macro_metavar_expr)] + +macro_rules! foo_error { + () => { + macro_rules! bar_error { + ( $( $any:tt )* ) => { $( $any )* }; + // ^^^^^^^^^^^ error: attempted to repeat an expression containing no syntax variables matched as repeating at this depth + } + }; +} + +macro_rules! foo_ok { + () => { + macro_rules! bar_ok { + ( $$( $any:tt )* ) => { $$( $any )* }; + } + }; +} + +fn main() { + foo!(); +} +``` + +One consequence of such expansion is that deeper nested levels make dollar-dollar declarations grown linearly, starting at `$$`, then `$$$$`, then `$$$$$` and so on. This is also necessary to be fully featured so that it is possible to specify names of metavariables using other metavariables at each nesting level. + +``` +$foo => bar => bar // Evaluate foo at level 1 +$$foo => $foo => bar // Evaluate foo at level 2 +$$$foo => $bar => baz // Evaluate foo at level 1, and use that as a name at level 2 +$$$$foo => $$foo => $foo // Evaluate foo at level 3 +$$$$$foo => $$bar => $bar // Evaluate foo at level 1, and use that as a name at level 3 +$$$$$$foo => $$$foo => $bar // Evaluate foo at level 2, and use that as a name at level 3 +$$$$$$$foo => $$$bar => $baz // Evaluate foo at level 1, use that at level 2, and then use *that* at level 3 +``` + ## Scoping, Exporting, and Importing For historical reasons, the scoping of macros by example does not work entirely