rust-macros · git:20261011.8d24f4c · 2026-10-11 · sha256 77d3ec037b093e42
rust-macros git:20261011.8d24f4cA
Immutable. This exact content never changes and is served at /api/v1/blob/77d3ec037b093e42.
---
name: rust-macros
description: >
Use when writing or debugging Rust macro_rules! matchers, repetitions, hygiene,
$crate exports, edition-dependent fragments, or procedural derive, attribute,
and function-like macros using syn and quote; includes expansion and diagnostic tests.
---
# Rust Macros
## Quick reference
| Task or symptom | Action | Load |
|---|---|---|
| Ordinary reusable logic | Use a function; generics for type abstraction | Optional rust-idiomatic or rust-api-design skill |
| Variable syntax, repeated declarations | Start with `macro_rules!` and an explicit grammar | [Declarative macros](references/declarative.md) |
| `local ambiguity`, invalid fragment follower | Add a delimiter/tag; inspect fragment follow sets | [Declarative macros](references/declarative.md) |
| Works locally, fails downstream | Inspect `$crate`, helper visibility, imports, renamed dependencies | [Declarative macros](references/declarative.md) |
| Derive, item transformation, parsed DSL | Separate proc-macro crate; parse → validate → generate | [Procedural macros](references/procedural.md) |
| Wrong bound or inscrutable derive error | Preserve generics; field-specific bounds and input spans | [Procedural macros](references/procedural.md) |
| Suspected bad expansion | Compile smallest caller, inspect expansion, test semantics | [Testing and traps](references/testing.md) |
| Edition migration changes matching | Check definition crate's edition, `expr_2021`, `pat_param` | [Declarative macros](references/declarative.md) |
## Scope and defaults
Macros are syntax APIs, not a general substitute for functions. Choose one only
when callers need syntax a function cannot accept or genuinely repetitive item
generation. A macro expands before type checking its generated code; expansion
itself does not know inferred types. Generated code can allocate, panic, move
values, or be expensive. No blanket runtime or build-time speed claim follows.
Read the project's edition, `rust-version`, toolchain and dependency versions
before choosing syntax. Examples here target edition 2024 (Rust 1.85 minimum);
this is not permission to raise a consumer's MSRV. Proc-macro dependencies and
optional debugging tools have their own MSRV constraints.
## Workflow
1. **Write the equivalent ordinary Rust.** Identify exactly which syntax must
vary. Keep runtime logic in normal functions rather than cloning it into
every expansion.
2. **Specify the public contract.** List accepted forms, empty input, trailing
comma policy, duplicate handling, evaluation count/order, ownership, and the
output position: expression, item, type, or pattern.
3. **Choose the least powerful mechanism.** `macro_rules!` for grammar matching
and repetition; derive to append implementations; attribute to transform an
item; function-like proc macro for syntax requiring a real parser.
4. **Make parsing unambiguous.** Specific rules precede catch-alls. Delimit
repeated groups explicitly. Reject unsupported input rather than silently
ignoring tokens or attributes.
5. **Protect caller context.** Use `$crate` for declarative helpers and qualified
library paths. Proc macros need explicit path/name handling. Never assume a
trait, `Vec`, or a helper is imported by the caller.
6. **Preserve semantics.** Evaluate an expression once unless repetition is
documented; preserve moves, borrows, early returns, `?`, attributes, and
generics. An expression expansion's final value must not gain a semicolon.
7. **Make failures useful.** For declarative grammar errors use a specific
fallback `compile_error!` when useful. For proc macros return spanned
`syn::Error`, not a panic caused by malformed user input.
8. **Test the caller, not just token text.** Include behavior assertions,
intentional compile failures, downstream imports and edition-sensitive forms.
Consult [testing](references/testing.md) for the matrix and tools.
## A small, executable syntax contract
This macro is deliberately a non-empty list, permits a trailing comma, evaluates
each argument once from left to right, and uses no heap allocation. Empty input
is not silently assigned an arbitrary element type. The normal array syntax
would suffice in production; this is a grammar/evaluation test, not a reason to
introduce an array wrapper API.
```rust
macro_rules! nonempty_array {
($first:expr $(, $rest:expr)* $(,)?) => {
[$first $(, $rest)*]
};
}
let mut visits = 0;
let values = nonempty_array!(
{ visits += 1; visits },
{ visits += 1; visits },
);
assert_eq!(values, [1, 2]);
assert_eq!(visits, 2);
assert_eq!(nonempty_array!(7), [7]);
```
The empty invocation intentionally fails because no first expression exists:
```rust,compile_fail
macro_rules! nonempty_array {
($first:expr $(, $rest:expr)* $(,)?) => {
[$first $(, $rest)*]
};
}
let _ = nonempty_array!();
```
## Pitfalls to check before expanding the design
- **Matching is not overload resolution.** First matching arm wins. If its
expansion fails type checking, later arms are not tried.
- **Fragments are not textual substitution.** An `expr` preserves expression
grouping and becomes opaque when forwarded; `tt` preserves token structure.
- **Hygiene is mixed-site for `macro_rules!`.** Locals/labels and item names do
not all resolve alike. Exporting a macro does not export its private helpers.
- **Repetition has structure.** Match and transcribe variables at compatible
nesting; zipped repetitions require equal lengths. Avoid recursive token-tree
munchers when an ordinary repetition expresses the grammar.
- **Edition belongs to the definition.** A 2024 caller does not upgrade a macro
defined in a 2021 dependency.
- **Derive appends, attribute replaces.** Returning the input from a derive
duplicates the original type. Failing to return an attribute's original or
transformed item deletes it.
- **Strings are not identifiers.** `quote!` interpolates a Rust string as a
literal; use parsed `Ident` or `format_ident!` for generated names.
- **Compile-time execution is not sandboxing.** Procedural dependencies execute
with compiler access to files/environment; review them like build scripts.
- **Expansion output is lossy.** `cargo expand` is a debugging aid, not a source
replacement or evidence of hygiene/behavior equivalence.
## Completion criteria
- Accepted syntax and intentional rejections are documented with real callers.
- Repetition boundaries, evaluation count, hygiene, and generics have evidence.
- Public macros work outside the defining module/crate with intended imports;
helper paths remain valid when the crate is renamed by Cargo.
- Diagnostics point to offending input, not only generated code.
- Required features, supported editions, and MSRV are explicit; optional tools
are not assumed installed.
- Report executed checks separately from suggested checks; never claim a token
snapshot proves the generated program works.
## Handoffs and sources
If installed, use **rust-tdd** for red–green–refactor and test-suite design,
**rust-api-design** for generated public contracts, **rust-idiomatic** for normal
traits/const/Cargo semantics, and **rust-performance** for measured expansion,
build-time or runtime costs. These are optional, not installation dependencies.
This skill adapts StructWeave chapters 14 and the macro/compile-time section of
13. Pinned upstream links, first-party references, and correction notes appear
in [README](README.md) and each reference. Read only the reference needed for
the current task.