When names need to fuse
You're writing a derive macro. You have a struct named User. Your goal is to generate a companion struct called UserSerializer. You write the macro logic, interpolate the name, and hit a wall. The compiler complains. You can't just add strings together inside a macro. Tokens aren't strings. You need to paste tokens. That's where the paste crate comes in. It lets you concatenate identifiers, lifetimes, and other tokens at compile time.
Token pasting explained
Rust macros work with tokens, not text. A token is a unit of syntax: an identifier, a keyword, a punctuation mark. When you write a macro, you manipulate these tokens. The quote crate lets you build token streams by interpolating values. If you interpolate #name, you get the token User. If you interpolate #suffix, you get Serializer. But you get two separate tokens. The compiler sees User Serializer, which is invalid syntax for a single identifier. You need UserSerializer.
Token pasting fuses two tokens into one. The paste crate provides a macro that performs this fusion during macro expansion. It runs before the compiler checks the syntax of the generated code. Rust forbids automatic pasting for a reason. If the compiler fused tokens silently, you could accidentally create a name that clashes with a keyword or a variable in the caller's scope. Explicit pasting makes the macro author responsible for the generated name. It keeps hygiene intact.
Think of tokens as Lego bricks. quote hands you the bricks. paste is the heat gun that melts two bricks together into a single, custom shape. You control the fusion. The result is a new brick that the rest of the code treats as a primitive.
Minimal example
The paste macro takes a block of code. Inside the block, you use interpolation syntax to mark where tokens should fuse. Spaces between pasted tokens are ignored. The result is a single token.
use paste::paste;
fn main() {
let name = "User";
paste! {
// The #name interpolates the identifier "User".
// The space between #name and Serializer is consumed.
// The result is a single struct named UserSerializer.
struct #name Serializer;
}
// This compiles because UserSerializer exists.
let _instance = UserSerializer;
}
Don't fight the token stream. Paste the name and move on.
How expansion works
When the compiler encounters paste!, it invokes the macro. The macro scans the block for interpolation markers like #name. It replaces those markers with the actual tokens from the context. Then it concatenates adjacent tokens. The output is a new token stream. This stream replaces the paste! call. The compiler continues with the fused tokens.
If you paste User and Serializer, the compiler sees UserSerializer. It's as if you typed it. The fusion happens before type checking. If you paste a lifetime 'a and an identifier ref, you get 'aref. The paste macro handles the token tree structure. It ensures the resulting token is valid. If you paste incompatible tokens, the macro fails or produces invalid syntax. The compiler will reject the output.
Convention: Nest paste! inside quote!. Most macro authors write quote! { paste! { ... } }. This keeps the token generation logic in one place. The paste macro expands after quote emits it. The quote crate outputs a token stream containing the paste! invocation. The compiler then expands paste!. This two-step expansion is standard practice.
Realistic derive macro
You're building a derive macro that generates a builder pattern. The macro needs to create a builder struct named UserBuilder and implement methods on it. You use paste to fuse the base name with the suffix.
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};
use paste::paste;
/// Derives a builder pattern for the struct.
#[proc_macro_derive(Builder)]
pub fn builder_derive(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = &input.ident;
// Generate the builder struct and methods.
let generated = quote! {
// Use paste to create the builder struct name.
// paste! fuses #name and Builder into one identifier.
paste! {
pub struct #name Builder {
// Fields would go here.
}
impl #name Builder {
pub fn new() -> Self {
Self {}
}
pub fn build(self) -> #name {
// Construct the original type.
#name {}
}
}
}
impl #name {
pub fn builder() -> #name Builder {
#name Builder::new()
}
}
};
generated.into()
}
Trust the borrow checker. It usually has a point.
Pitfalls and errors
Pasting can break hygiene. If you paste a token from the macro input with a token from the macro definition, the resulting token might have mixed hygiene. The compiler might reject it. You'll see E0425 (cannot find value) or E0433 (failed to resolve). The fix is often to ensure both tokens come from the same context or use paste carefully.
If you forget paste! and just interpolate adjacent tokens, quote emits them separately. The compiler sees struct User Serializer. It interprets struct User as a complete declaration. Then it sees Serializer and chokes. You get E0422 (expected struct, variant or union type) or a syntax error. The compiler thinks you wrote two statements without a semicolon. Wrap the block in paste! to fuse the name.
You can't paste arbitrary punctuation. paste is designed for identifiers, lifetimes, and generic parameters. Trying to paste operators or keywords usually fails or produces invalid syntax. The macro doesn't support fusing + and + to create ++. Rust doesn't have that operator anyway. Stick to names and lifetimes.
Convention: Use #name interpolation in paste. The crate supports the same #name syntax as quote. Stick to that. Avoid the older [<>] bracket syntax unless you're maintaining legacy code. The #name form is clearer and integrates better with quote.
If the compiler sees two tokens where one should be, wrap it in paste!.
When to use paste
Use paste when you need to concatenate identifiers, lifetimes, or generic parameters to form a single token. Use paste when generating companion types like UserBuilder or UserSerializer from a base name. Use paste inside quote! blocks to fuse tokens before the compiler sees them. Reach for plain quote interpolation when you need to insert a token without modification. Reach for string formatting when you are constructing error messages or documentation strings, not Rust code. Pick paste over manual token manipulation when you want readable macro code; paste handles the token fusion syntax for you.
Paste for fusion. Quote for assembly. Keep them distinct.