Token Streams
A TokenStream (proc_macro::TokenStream or proc_macro2::TokenStream) is the fundamental input and output currency of procedural macros: an ordered sequence of TokenTree nodes produced by the compiler's lexer.
The Four Variants of TokenTree
Instead of passing raw source characters (&str) or a flat list of parentheses to macros, the Rust lexer groups balanced delimiters ((), [], {}) into nested Group trees before invoking any macro:
pub enum TokenTree {
Group(Group), // Balanced `( ... )`, `[ ... ]`, or `{ ... }` containing an inner TokenStream
Ident(Ident), // Identifier or keyword: `struct`, `Point`, `x`, `u32`
Punct(Punct), // Single punctuation character: `+`, `-`, `:`, `;`, `>` (with Spacing::Joint or Alone)
Literal(Literal), // String, char, byte, integer, or float literal: `"hi"`, `42`, `3.14`
}
Source Code: `fn add(a: i32) -> i32 { a + 1 }`
TokenStream (7 top-level TokenTrees):
[0] Ident("fn")
[1] Ident("add")
[2] Group(Parenthesis, TokenStream: [ Ident("a"), Punct(':'), Ident("i32") ])
[3] Punct('-', Spacing::Joint)
[4] Punct('>', Spacing::Alone)
[5] Ident("i32")
[6] Group(Brace, TokenStream: [ Ident("a"), Punct('+'), Literal(1) ])
Notice that multi-character operators like ->, ::, +=, and && are represented as consecutive Punct tokens whose Spacing is Spacing::Joint on all characters except the final one (Spacing::Alone).
Every TokenTree Carries a Span
Every Group, Ident, Punct, and Literal inside a TokenStream carries a Span recording its exact source file, byte range, and hygiene context. When a procedural macro constructs new tokens (or emits a compile_error!) and attaches the input token's Span, compiler diagnostics underline the exact offending token in the user's source file rather than pointing blindly to the macro call as a whole.
Constructing Token Streams with quote!
Writing TokenTree::Ident(...) and TokenTree::Punct(...) calls manually is verbose. In practice, procedural macros use the quote::quote! macro to construct a proc_macro2::TokenStream using #var interpolation and #(#slice)* repetition:
// Inside a procedural macro using `quote`:
/*
let field_names = [&ident_a, &ident_b];
let tokens = quote! {
impl Summary for #struct_name {
fn fields(&self) -> &[&'static str] {
&[ #( stringify!(#field_names) ),* ]
}
}
};
*/
// Resulting Rust code produced by the TokenStream above:
struct Record {
id: u64,
active: bool,
}
trait Summary {
fn fields(&self) -> &[&'static str];
}
impl Summary for Record {
fn fields(&self) -> &[&'static str] {
&[stringify!(id), stringify!(active)]
}
}
fn main() {
let rec = Record {
id: 1,
active: true,
};
println!(
"Record (id={}, active={}) fields: {:?}",
rec.id,
rec.active,
rec.fields()
);
}
Record (id=1, active=true) fields: ["id", "active"]