Use this tag for questions about procedural macros, declared in crates of the proc-macro type, of the Rust programming language.
The Rust compiler (rustc) can accept various plugins called procedural macros to modify the user's code and generate it at compile-time.
The documentation can be found in the official book.
The kinds of procedural macros
There are 3 kinds of procedural macros.
The custom derives
They are used as following:
#[derive(MyCustomDerive)]
struct Foo {
// ...
}
This kind of macros applies to a decorated struct or enum and are intended to generate some code without modifying the user's code, typically to generate an implementation of a trait.
There are some examples of such macros in the std crate, for example to generate the implementation of the Debug or the PartialEq traits.
The custom attributes
They are used as following:
#[my_custom_attribute(optional_parameter = "value")]
fn some_function() {
// ...
}
They can modify arbitrarily any Rust item: a struct, a function, a module, etc.
Function-like macro
They are used as following:
my_function_like_macro!(some arbitrary input);
It can accept any input to generate some Rust code: the only limit of the input format is the imagination of the macro developer.
How to create a procedural macro
A procedural macro must live in its own crate. A special line must be added in the Cargo.toml manifest:
[lib]
proc-macro = true
The following dependencies are not mandatory, but are widely used because of the useful things they bring:
proc-macro2let use some unstableproc-macrothings inside a project compiled in stable;quotelet generate some Rust code easily;synis a Rust source parser.
The macros must be declared in the root module (i.e. in the lib.rs file), for example for a custom attribute:
#[proc_macro_attribute]
pub fn fact_inner(args: TokenStream, input: TokenStream) -> TokenStream {
/// ...
}
Every procedural macro returns a TokenStream being the generated code.
The functions for each kind of procedural have a different signature:
Custom derive
A custom derive must be declared as following:
#[proc_macro_derive(MyDerive)]
pub fn my_derive(input: TokenStream) -> TokenStream {
// ...
}
input is the user defined struct or enum being decorated.
A typical implementation will look like the following:
#[proc_macro_derive(MyDerive)]
pub fn my_derive(input: TokenStream) -> TokenStream {
// Parse the input as a struct:
let item = syn::parse_macro_input!(input as syn::ItemStruct);
// Or if you want to decorate an enum:
let item = syn::parse_macro_input!(input as syn::ItemEnum);
// Get the generated code, or transform an error into a compile error:
let output = my_derive_generate(item).unwrap_or_else(|err| err.to_compile_error());
TokenStream::from(output)
}
fn my_derive_generate(input: syn::ItemStruct) -> Result<proc_macro2::TokenStream, syn::parse::Error> {
// ...
}
You can accept attributes in the user defined code:
#[proc_macro_derive(MyDerive, attributes(my_attribute))]
pub fn my_derive(input: TokenStream) -> TokenStream {
// ...
}
Example of accepted code from the user:
#[derive(MyDerive)]
struct Foo {
#[my_attribute("any content")]
bar: i32,
}
Custom attribute
It must be declared like this:
#[proc_macro_attribute]
pub fn my_attribute(args: TokenStream, input: TokenStream) -> TokenStream {
// ...
}
args is the arguments of the attribute, while input is the user's input where the macro applies.
Here are the valid attribute contents:
- No content:
#[attribute] - Name = value:
#[attribute = "a literal"] - A literal:
#[attribute("a literal")] - A list:
#[attribute(Ident1, Ident2)] - A list of key/value:
#[attribute(key = literal1, key = literal2)]
A typical implementation will look like the following:
#[proc_macro_attribute]
pub fn my_attribute(args: TokenStream, input: TokenStream) -> TokenStream {
// Parse the attribute arguments:
let args = syn::parse_macro_input!(args as syn::AttributeArgs);
// Parse the input (for example):
let item = syn::parse_macro_input!(input as syn::ItemFn);
// Get the generated code, or transform an error into a compile error:
let output = my_attribute_generate(item).unwrap_or_else(|err| err.to_compile_error());
TokenStream::from(output)
}
fn my_attribute_generate(args: syn::AttributeArgs, input: syn::ItemStruct)
-> Result<proc_macro2::TokenStream, syn::parse::Error> {
// ...
}
Function-like macro
It must be declared like this:
#[proc_macro]
pub fn my_proc_macro(input: TokenStream) -> TokenStream {
// ...
}