In this lesson, you will learn how to handle attribute collisions by implementing a priority-based dispatch system for custom procedural macro attributes.
The Conflict of Attribute Overloading
When building complex systems, you may find yourself in a scenario where multiple macros attempt to target the same item, or where a single attribute is intended to behave differently based on the metadata provided. In Rust, procedural macros are evaluated by the compiler in a specific order, but when two macros compete for the same declarative space, simple resolution isn't enough. We solve this by implementing a Priority-Based Dispatcher.
The Dispatch Pattern
Instead of writing monolithic macros that check for every possible configuration, you create a "Registry" macro. This acts as a gateway that inspects the TokenStream and forwards it to the most relevant sub-macro based on a defined precedence logic.
Consider an scenario where both #[log] and #[audit] are competing to instrument a function:
#[proc_macro_attribute]
pub fn secure(attr: TokenStream, item: TokenStream) -> TokenStream {
let input = parse_macro_input!(item as ItemFn);
// Check for internal metadata to determine priority
if has_high_security_requirement(&input) {
// Dispatch to the high-priority auditing macro
audit_macro::generate(attr, input)
} else {
// Fallback to the standard logging macro
log_macro::generate(attr, input)
}
}
By centralizing the logic, you avoid the "Attribute Collision" error where the compiler struggles to resolve multiple proc-macro invocations on a single struct or function.
Why This Matters
As your library grows, you will inevitably encounter cases where users apply multiple decorators. By using a dispatcher:
- Predictability: You define the execution order, preventing non-deterministic behavior.
- Maintenance: You keep individual macro logic decoupled, while the registry handles the "policy" of which macro wins.
- Collision Avoidance: You can explicitly define if two attributes are mutually exclusive or if they should be composed via a specific algebraic transformation.
This approach ensures that your macro infrastructure remains extensible without requiring users to worry about the order of attribute placement, which the compiler might otherwise handle inconsistently.
Key Takeaway: Use a centralized dispatch macro to inspect metadata and route target items, transforming collision-prone attributes into a deterministic priority-based pipeline.