Skip to content

Commit

Permalink
Add stable references of macro_metavar_expr
Browse files Browse the repository at this point in the history
  • Loading branch information
c410-f3r committed Apr 14, 2022
1 parent b5f6c23 commit 7de7529
Showing 1 changed file with 70 additions and 0 deletions.
70 changes: 70 additions & 0 deletions src/macros-by-example.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 7de7529

Please sign in to comment.