proc_macro_attribute
is a type of procedural macro in Rust that allows you to define custom attributes. These attributes can be attached to items like functions, structs, enums, and more. The macro can then manipulate or generate code based on the item it is attached to.
Key Concepts
Procedural Macros:
Procedural macros are unhygienic, meaning they behave as if the output token stream was written inline to the code it’s next to.
They can be function-like, derive, or attribute macros.
Attribute Macros:
Attribute macros define new outer attributes that can be attached to items.
They are defined by a public function with the
proc_macro_attribute
attribute and a signature of ``(TokenStream, TokenStream) -> TokenStream`.
Example of proc_macro_attribute
Let's create a custom attribute macro that logs the input and output token streams.
Create a new procedural macro crate:
cargo new my_macro --lib
cd my_macro
SH
Modify
Cargo.toml
:
[lib]
proc-macro = true
[dependencies]
proc-macro2 = "1.0"
quote = "1.0"
syn = "1.0"
TOML
Implement the attribute macro in
src/lib.rs
:
extern crate proc_macro;
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, ItemFn};
# [proc_macro_attribute]
pub fn log_streams(attr: TokenStream, item: TokenStream) -> TokenStream {
// Parse the input token stream as a function item
let input_fn = parse_macro_input!(item as ItemFn);
// Convert the function item back to a token stream for logging
let item_str = item.to_string();
let attr_str = attr.to_string();
// Log the input streams
println!("Attribute: {}", attr_str);
println!("Item: {}", item_str);
// Generate the output token stream
let expanded = quote! {
#input_fn
};
// Return the generated token stream
TokenStream::from(expanded)
}
RUST
Use the attribute macro in another crate:
cargo new my_project
cd my_project
SH
Modify Cargo.toml
to include the macro crate as a dependency:
[dependencies]
my_macro = { path = "../my_macro" }
TOML
Use the attribute macro in src/main.rs
:
extern crate my_macro;
use my_macro::log_streams;
# [log_streams]
fn my_function() {
println!("Hello, world!");
}
fn main() {
my_function();
}
RUST
When you run this code, the log_streams
macro will log the input attribute and item token streams to the console.
Explanation
Input Token Streams:
The first
TokenStream
(attr
) contains the tokens following the attribute name.The second
TokenStream
(item
) contains the tokens of the item the attribute is attached to.
Output Token Stream:
The macro generates a new
TokenStream
that replaces the original item. In this example, it simply returns the original item unchanged.
Use Case:
This example logs the token streams for debugging purposes. In a real-world scenario, you might generate additional code or modify the item based on the attribute.
Summary
proc_macro_attribute
allows you to define custom attributes that can manipulate or generate code based on the items they are attached to. By using procedural macros, you can create powerful tools for code generation and manipulation at compile time.