Skip to content

Commit c5a9df6

Browse files
committed
Add stable references of macro_metavar_expr
1 parent b74825d commit c5a9df6

File tree

1 file changed

+74
-0
lines changed

1 file changed

+74
-0
lines changed

src/macros-by-example.md

+74
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,80 @@ compiler knows how to expand them properly:
193193
not have the same number. This requirement applies to every layer of nested
194194
repetitions.
195195

196+
## Metavariable expressions
197+
198+
Metavariable expressions in declarative macros provide expansions for information about metavariables that are otherwise not easily obtainable.
199+
200+
| Expression | Meaning |
201+
|----------------------------|------------|
202+
| `${ignore(ident)}` | Binds `$ident` for repetition, but expands to nothing. |
203+
| `$$` | Expands to a single `$`, for removing ambiguity in recursive macro definitions. |
204+
205+
### Ignore
206+
207+
`${ignore(ident)}` repeats an expansion the same number of times a metavariable repeats without actually expanding the metavariable.
208+
209+
```rust
210+
macro_rules! count {
211+
( $( $ignored_identifier:ident ),* ) => {{
212+
0 $( + 1 ${ignore(ignored_identifier)} )*
213+
}};
214+
}
215+
216+
fn main() {
217+
assert_eq!(count!(T, T, T), 3);
218+
}
219+
```
220+
221+
`count!` 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.
222+
223+
### Dollar-dollar ($$)
224+
225+
`$$` expands to a single `$`.
226+
227+
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 be used to resolve ambiguities in nested macros.
228+
229+
The following example illustrates a macro that fails to compile due to the ambiguity of the repetition in the nested macro:
230+
231+
```rust,compile_fail
232+
macro_rules! foo_error {
233+
() => {
234+
macro_rules! bar_error {
235+
( $( $any:tt )* ) => { $( $any )* };
236+
// ^^^^^^^^^^^ error: attempted to repeat an expression containing no syntax variables matched as repeating at this depth
237+
}
238+
};
239+
}
240+
241+
foo_error!();
242+
```
243+
244+
The following resolves the problem by escaping the `$` in the repetition with `$$`:
245+
246+
```rust
247+
macro_rules! foo_ok {
248+
() => {
249+
macro_rules! bar_ok {
250+
( $$( $any:tt )* ) => { $$( $any )* };
251+
}
252+
};
253+
}
254+
255+
foo_ok!();
256+
```
257+
258+
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.
259+
260+
```
261+
$foo => bar => bar // Evaluate foo at level 1
262+
$$foo => $foo => bar // Evaluate foo at level 2
263+
$$$foo => $bar => baz // Evaluate foo at level 1, and use that as a name at level 2
264+
$$$$foo => $$foo => $foo // Evaluate foo at level 3
265+
$$$$$foo => $$bar => $bar // Evaluate foo at level 1, and use that as a name at level 3
266+
$$$$$$foo => $$$foo => $bar // Evaluate foo at level 2, and use that as a name at level 3
267+
$$$$$$$foo => $$$bar => $baz // Evaluate foo at level 1, use that at level 2, and then use *that* at level 3
268+
```
269+
196270
## Scoping, Exporting, and Importing
197271

198272
For historical reasons, the scoping of macros by example does not work entirely

0 commit comments

Comments
 (0)