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
7
votes
1 answer

What is a suitable place to store procedural macro artifacts on disk so that they get cleaned up by Cargo?

I have a procedural macro that needs to store semi-persistent state. The state needs to survive compilation runs and should be cleaned up when cargo clean is run. In the past, I used the ./target directory this purpose. However, this was a mistake…
Tenders McChiken
  • 1,216
  • 13
  • 21
6
votes
1 answer

Why can't Rust find method for enum generated using proc_macro_attribute?

I am trying to write procedural macros that will accept a Rust enum like #[repr(u8)] enum Ty { A, B } and generate a method for the enum that will let me convert an u8 into an allowed variant like this fn from_byte(byte: u8) -> Ty { …
Akritrime
  • 95
  • 1
  • 6
6
votes
2 answers

Can we get the source code location of the caller in a procedural macro attribute?

I have requirement to get the source location of the caller of every method. I am trying to create a proc_macro_attribute to capture the location and print it. #[proc_macro_attribute] pub fn get_location(attr: TokenStream, item: TokenStream) ->…
Ayush Mishra
  • 567
  • 1
  • 7
  • 19
6
votes
2 answers

How to find the correct return type for syn::parse?

I have a TokenStream that comes from the definition of a Rust function. I want to parse it with syn::parse (and .unrwap()). However, I don't know what the return type is supposed to be: let ast: ??? =…
Stephane Bersier
  • 710
  • 7
  • 20
6
votes
1 answer

What is this strange syntax where an enum variant is used as a function?

Below is the example given by the mod documentation of syn::parse. enum Item { Struct(ItemStruct), Enum(ItemEnum), } struct ItemStruct { struct_token: Token![struct], ident: Ident, brace_token: token::Brace, fields:…
updogliu
  • 6,066
  • 7
  • 37
  • 50
5
votes
2 answers

Is there a way to have a public trait in a proc-macro crate?

I have a proc-macro crate with a macro that, when expanded, needs to use custom trait implementations for Rust built-in types. I tried to define the trait in the same crate, but Rust tells me that a proc-macro crate can only have public macros (the…
jecolon
  • 188
  • 1
  • 7
5
votes
0 answers

How can I add rustdoc comments to proc macro-generated code?

I wrote a proc-macro library to accompany a library I'm writing - they go together, with the former being used to abstract away a lot of redundant code in the latter. I am successfully generating several structs and impls in my proc macro. I am also…
user655321
  • 1,572
  • 2
  • 16
  • 33
5
votes
0 answers

How can the compilation properties be determined in a procedural macro?

I'm working on a procedural macro that does a lot of work that can slow down compilation considerably. The work done does not effect the semantics of the function; that is, if given the same set of arguments, the returned value is does not change…
Tenders McChiken
  • 1,216
  • 13
  • 21
5
votes
1 answer

Is it possible to tell if a field is a certain type or implements a certain method in a procedural macro?

I created a procedural macro that implements a trait, but in order for this to work I need to get the raw bytes for every field. The problem is how to get the bytes of a field differs depending on the type of field. Is there some way of testing if…
blackwolfsa
  • 327
  • 3
  • 14
5
votes
0 answers

How do I write a procedural macro that expands to a macro invocation without requiring users to import the crate for the macro?

I'm trying to write a function-like procedural macro my_macro that expands to a lazy_static macro invocation. I want to write it in a way that users of my_macro don't need to list lazy_static in their crate's dependencies and explicitly use it (use…
Felix
  • 6,131
  • 4
  • 24
  • 44
4
votes
0 answers

Extracting generics for field type from parent generics

I am in the process of writing a proc-macro attribute which will be placed on structs and enums, but I am running into an issue with generics. I derive functionality for each field separately, so I need to get the generics required for a single…
Locke
  • 7,626
  • 2
  • 21
  • 41
4
votes
1 answer

Is there any way to get a trace of where an error occurs within a proc macro?

I am implementing a proc macro, and testing in another crate. When I compile the client crate, there is an error thrown from the invocation site of the proc macro: error: proc macro panicked --> foo/src/main.rs:17:1 The error is occurring within…
sak
  • 2,612
  • 24
  • 55
4
votes
1 answer

Proc macro execution order

I have one proc macro that looks like this: #[proc_macro_attribute] pub fn my_macro(_meta: CompilerTokenStream, input: CompilerTokenStream) -> CompilerTokenStream { //* bits of code */ } Then I have a derive…
Alex Vergara
  • 1,766
  • 1
  • 10
  • 29
4
votes
1 answer

How do I accept syntactically invalid input in a procedural macro?

I thought that procedural macros only had to have lexically-valid input, but it seems like all input must also parse as Rust code, unless I'm doing something wrong. The RFC says: By operating on tokens, code passed to procedural macros does not…
Xavier Denis
  • 406
  • 3
  • 14
4
votes
2 answers

How can I parse an attribute with nested arguments with darling?

I'm trying to parse an attribute with darling, and I want to support the following usages: // att not specified #[derive(MyTrait)] struct Foo(u64); // att specified without an argument #[derive(MyTrait)] #[myderive(att)] struct Foo(u64); // att…
Peter Hall
  • 53,120
  • 14
  • 139
  • 204
1
2
3
8 9