Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Meta transformers

API reference on docs.rs: reflect!, perm!.

Meta transformers operate on invocation objects and token-stream structure rather than assuming that their input is a Rust item or expression.

Reflecting invocations

reflect! accepts two macro paths and an opaque body:

macro_rules! add_one {
    ($expression:expr) => { 1 + $expression };
}

macro_rules! double {
    ($expression:expr) => { 2 * $expression };
}

let reflected = reflect!(add_one, double; 3);
assert_eq!(reflected, 8);

It performs only the structural reflection

first! { second! { body } }
    ↦ second! { first! { body } }

so the example becomes double! { add_one! { 3 } }. The inner body is never parsed by reflect!, and both macro paths retain their call-site spans. This is not an API for forcing another macro to expand inside the current procedural macro; it constructs the reflected invocation objects and lets rustc expand them normally afterward.

Internally the implementation is exactly the composition

invoke(second, invoke(first, body))

where invoke pairs one macro path with one opaque token stream.

Outer attributes may precede either path and move together with that invocation:

reflect!(#[a] first, #[b] second; body)
    -> #[b] second! { #[a] first! { body } }

Whether a particular attribute is valid still depends on the Rust context in which the resulting invocation appears. The input describes invocation paths rather than parsing two complete existing macro calls, so reflect! always constructs braced invocations and does not preserve operand delimiters or trailing semicolons. An outer attribute written before reflect! itself belongs to the reflect! call, not either operand.

Permuting token trees

perm! uses ordinary one-based cycle notation. Separate positions with spaces: 14 is one integer token, whereas 1 4 denotes two positions.

perm! { ((1 4 3)), a, b, c, d, e }
    -> c, b, d, a, e

Cycles compose from right to left. The permutation acts by moving the token tree at position i to position σ(i). Positions beyond the largest index appearing in any cycle remain fixed, so the same notation implicitly embeds S_n into every S_{n+k}.

The implementation stores the destination of each source position only through the final non-fixed point. Therefore the identity needs no stored entries, a cycle constructs one finite map, and multiplication is ordinary function composition:

(sigma * tau)(i) = sigma(tau(i))

This representation is also what applies a written cycle product to the token trees, so the algebra exercised by the implementation tests and the documented right-to-left expansion rule are the same code path.

Each comma-delimited operand must be exactly one token tree: an identifier, literal, punctuation token, or one delimited group. Put a multi-token operand in a group if it should move as one unit.

The expansion is the raw comma-separated stream, deliberately without a tuple, array, block, or other imposed Rust shape. This makes perm! a structural building block, but its invocation must ultimately occur in a context whose consumer accepts that output shape.

Run the two complete programs shipped with the crate using cargo run --example meta_transformers and cargo run --example enum_fn.