#[proc_macro] / #[proc_macro_derive(...)] / #[proc_macro_attribute]Attribute
Explanation
These three attributes register an ordinary function as a specific kind
of procedural macro. Each requires the crate it's defined in to declare
proc-macro = true under [lib] in Cargo.toml; such a crate may
export only functions tagged with one of these three (plus private
helper items) — no ordinary public functions or types alongside them.
The function itself must be pub and return TokenStream.
#[proc_macro]— registers a function-like macro. Signature:fn(TokenStream) -> TokenStream. Invoked at the call site asname!(...), the same syntax as amacro_rules!macro. See Function-like macros for what this kind is for.#[proc_macro_derive(TraitName)]— registers a derive macro as the implementation behind#[derive(TraitName)]. Signature:fn(TokenStream) -> TokenStream. Optionally takes a second argument,attributes(helper_one, helper_two, ...), which reserves one or more otherwise-inert helper attribute names (e.g.#[proc_macro_derive(Reading, attributes(unit))]reserves#[unit(...)]) as legal to write inside the annotated item, for the derive function itself to inspect via itsTokenStreaminput. See Derive macros.#[proc_macro_attribute]— registers an attribute-like macro. Signature:fn(TokenStream, TokenStream) -> TokenStream— the attribute's own arguments, then the full annotated item. Invoked as#[name]or#[name(...)]above an item. See Attribute-like macros.
A given function may carry only one of these three; a single
proc-macro = true crate is free to export any mix of them, each its
own separately tagged function. This page covers only the registration
syntax itself — see Procedural macros
for the shared token-stream-in/token-stream-out mental model, and the
kind-specific concept pages above for what each macro kind is actually
for and how it's used well.
Usage examples
Registering all three procedural macro kinds
use proc_macro::TokenStream;
#[proc_macro] // <- registers a function-like macro, invoked as `build!(...)`
pub fn build(input: TokenStream) -> TokenStream { input }
#[proc_macro_derive(Reading)] // <- registers a derive macro, invoked as `#[derive(Reading)]`
pub fn derive_reading(input: TokenStream) -> TokenStream { TokenStream::new() }
#[proc_macro_attribute] // <- registers an attribute-like macro, invoked as `#[traced]`
pub fn traced(_attr: TokenStream, item: TokenStream) -> TokenStream { item }
Designing a public API
A macro crate for a sensor-data library offers all three forms from one
proc-macro = true crate: a derive for boilerplate trait impls, an
attribute for wrapping handler functions, and a function-like macro for
validated literals — each function tagged with the registration
attribute matching what it needs to receive.
// sensorkit-macros/Cargo.toml
// [lib]
// proc-macro = true
use proc_macro::TokenStream;
#[proc_macro_derive(Reading, attributes(unit))] // <- also reserves the inert helper attribute #[unit(...)]
pub fn derive_reading(input: TokenStream) -> TokenStream {
TokenStream::new() // a real implementation inspects `input`'s fields here
}
#[proc_macro_attribute] // <- receives the attribute's own args AND the annotated item, as two streams
pub fn traced(_attr: TokenStream, item: TokenStream) -> TokenStream {
item
}
#[proc_macro] // <- receives only the tokens written inside its own `!(...)` invocation
pub fn sql(input: TokenStream) -> TokenStream {
input // a real implementation validates the embedded SQL text here
}
Each registration attribute fixes the function's exact
signature and what it's invoked as, so choosing between them is really
choosing which of the three token-stream contracts a given macro needs —
the Rust Reference's procedural macros chapter
documents all three signatures, and a single proc-macro = true crate
is free to mix any number of each, since the registration is per
function, not per crate.