Questions tagged [rust-proc-macros]

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-macro2 let use some unstable proc-macro things inside a project compiled in stable;
  • quote let generate some Rust code easily;
  • syn is 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 {
    // ...
}
126 questions
86
votes
4 answers

How do I see the expanded macro code that's causing my compile error?

I have a compile error involving a macro: :6:19: 6:50 error: cannot move out of captured outer variable in an `FnMut` closure :6 bind ( $ e , move | $ p | mdo ! { $ ( $ t ) * } ) ) ; ( …
Caspar
  • 7,039
  • 4
  • 29
  • 41
32
votes
3 answers

How to report errors in a procedural macro using the quote macro?

I am writing a procedural macro which works fine, but I am having trouble reporting errors in an ergonomic way. Using panic! "works" but is not elegant and does not present the error message to the user nicely. I know that I can report good errors…
Peter Hall
  • 53,120
  • 14
  • 139
  • 204
30
votes
2 answers

How to programmatically get the number of fields of a struct?

I have a custom struct like the following: struct MyStruct { first_field: i32, second_field: String, third_field: u16, } Is it possible to get the number of struct fields programmatically (like, for example, via a method call…
Akiner Alkan
  • 6,145
  • 3
  • 32
  • 68
20
votes
1 answer

Is it possible to store state within Rust's procedural macros?

Is it possible to build a macro that doesn't output anything but instead for example stores state to build up a list and then a second macro that will then actually use that data and output some result? For example: trait SomeTrait…
Thermatix
  • 2,757
  • 21
  • 51
16
votes
4 answers

How do I get the value and type of a Literal in a procedural macro?

I am implementing a function-like procedural macro which takes a single string literal as an argument, but I don't know how to get the value of the string literal. If I print the variable, it shows a bunch of fields, which includes both the type and…
anon
14
votes
2 answers

Is there a way to get the file and the module path of where a procedural macro is attached at compile-time?

I'm looking for the equivalent of file!() & module_path!() in a procedural macro context. For example, the following doesn't work: file.rs: #[some_attribute] const A: bool = true; macro.rs: #[proc_macro_attribute] pub fn some_attribute(attr:…
tversteeg
  • 4,717
  • 10
  • 42
  • 77
12
votes
1 answer

How do I make my custom derive macro accept trait generic parameters?

I am trying to implement custom derive macros for my traits, and they actually work! However I have a slight problem. I can't seem to find a way to include generic parameters to the trait. Specifically, I want to do something like this :…
user7924331
12
votes
2 answers

How do I create a proc_macro_attribute?

Now that proc_macros have been stabilized, how does one create such a thing? From what I've seen, there's the option of putting a #[proc_macro_attribute] annotation on a fn whatsitsname(attrs: TokenStream, code: TokenStream) -> TokenStream, but how…
llogiq
  • 13,815
  • 8
  • 40
  • 72
11
votes
2 answers

How can I get the T from an Option when using syn?

I'm using syn to parse Rust code. When I read a named field's type using field.ty, I get a syn::Type. When I print it using quote!{#ty}.to_string() I get "Option". How can I get just "String"? I want to use #ty in quote! to print "String"…
Đorđe Zeljić
  • 1,689
  • 2
  • 13
  • 30
10
votes
2 answers

How do I create a function-like procedural macro?

How should a_proc_macro be defined so it "returns" a 5? fn main() { let a = a_proc_macro!(); assert!(a == 5); }
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
9
votes
2 answers

How can I create hygienic identifiers in code generated by procedural macros?

When writing a declarative (macro_rules!) macro, we automatically get macro hygiene. In this example, I declare a variable named f in the macro and pass in an identifier f which becomes a local variable: macro_rules! decl_example { …
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
9
votes
1 answer

Cyclic package dependency while implementing proc macro

I try to implement a proc_macro Dump, which is similar to serdes Serialize. For this purpose I have a crate foo which contains my "primitive" structs (P1 and P2 in this case) which should only be dumpable. Next I do have a foo_derive crate which…
hellow
  • 12,430
  • 7
  • 56
  • 79
8
votes
1 answer

How do I check if a type implements a trait when implementing a proc macro?

I'm trying to create a proc-macro to derive and implement a trait for structs and I need that all fields within the struct implement Display. How do I check that? And furthermore how do I check if an attribute implements Iterator as well? (I want to…
Augusto
  • 1,234
  • 1
  • 16
  • 35
8
votes
1 answer

Convert string into TokenStream

Given a string (str), how can one convert that into a TokenStream in Rust? I've tried using the quote! macro. let str = "4"; let tokens = quote! { let num = #str; }; // #str is a str not i32 The goal here is to generate tokens for some unknown…
lovelikelando
  • 7,593
  • 6
  • 32
  • 50
7
votes
0 answers

Call macro inside/before other macro

I have created a function-like procedural macro. I call it with a declarative macro as parameter. I was hoping to find a way that the declarative macro is expanded before processing the procedural macro (or expand it inside the procedural macro).…
J. Doe
  • 12,159
  • 9
  • 60
  • 114
1
2 3
8 9