liveBook · Manning

Attribute macro

As explained in Write Powerful Rust Macros

An attribute macro is a Rust procedural macro that defines a custom attribute and replaces the annotated item with generated output.

An attribute macro is a Rust procedural macro that defines a custom attribute such as #[public] and replaces the item it annotates with generated tokens. It can annotate structs, enums, unions, traits, or functions, so it can change existing definitions rather than merely add to them. section 1.3.1 section 4.10

Its entry point is marked #[proc_macro_attribute] and receives two TokenStreams: attribute arguments and the annotated item. Returning an empty token stream removes that item entirely, making replacement behavior central to both implementation and debugging. section 4.2

A practical implementation follows this path:

  • Parse the item into a syn AST, often DeriveInput for structs, enums, and unions, or a more specific item type when needed. section 4.4
  • Inspect the relevant AST nodes, such as DataStruct and named Fields, then preserve or alter the parts the macro owns. section 4.4
  • Generate replacement tokens with quote!, recreating the item and any changed fields, methods, or declarations. section 4.10 section 10.2
  • Parse optional attribute arguments from the first TokenStream when custom configuration is needed. section 8.7

Making selected struct fields public

#[public(exclude(fourth, third))] can transform a struct so unexcluded fields become pub while excluded fields retain their original visibility. The macro parses the exclude list, walks the struct fields, and quotes a replacement struct. section 8.7 section 4.4

Choose an attribute macro when code must be altered. Prefer a derive macro when only adding functionality to a struct, enum, or union; use a function-like macro when transformation must apply more broadly. section 5.4

Attribute arguments are flexible and active: the macro can accept custom syntax in its annotation, and the processed annotation removes itself. This enables configurable transformations, but also makes clear parsing and useful compile errors important. section 8.11 section 7.0

Listing 4.3 Initial setup
extern crate core;

use quote::quote;
use proc_macro::TokenStream;
use syn::{parse_macro_input, DeriveInput};

#[proc_macro_attribute]             #1
pub fn public(_attr: TokenStream, item: TokenStream) #2
    -> TokenStream {              
    let _ast = parse_macro_input!(item as DeriveInput);     #3

    let public_version = quote! {}; #4

    public_version.into()
}

Inspect the minimal #[proc_macro_attribute] setup to see the two inputs, AST parsing, and why an empty quoted output makes the annotated struct disappear. section 4.2

How Write Powerful Rust Macros covers it

How deep it goes
Works through parsing struct fields, matching AST variants, quoting replacement output, and parsing configurable attribute arguments.
Examples
Demonstrates a public-fields struct transformation, configurable excluded fields, panic-to-Result function rewriting, and configuration-struct generation.
What it assumes
Assumes Rust familiarity while introducing procedural-macro TokenStreams, syn AST inspection, matching, and quote!-based output.
What you can do after
Build it from scratch by parsing annotated items and arguments, then generating replacement Rust tokens with quote!.
How it connects
Connects procedural macros with derive macros, function-like macros, TokenStream, syn, quote!, Result, and panic!.

Also explained in

Compare how 2 books explain attribute macro →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Write Powerful Rust Macros. The section links point to the parts of the book it draws on.