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
1
vote
2 answers

How to provide an enumerated index to a macro interpolation function in quote with proc_macro?

I've implemented the following proc_macro that takes builtin_method!(hello_world(a, b, c) { println!("{} {} {}", a, b, c); } and should generate pub fn hello_world(args: Vec) { let a = args.get(0).unwrap(); let b =…
glasflügel
  • 325
  • 2
  • 11
1
vote
0 answers

Is it possible to remove fields or replace an existing field in a Rust struct with macros?

I am trying to write an attribute macro for a proc-macro that would allow me to remove a field from a struct or completely replace its signature. Something similar to this: #[derive(my_macro)] struct OurStruct{ a: i32, …
Reza
  • 1,478
  • 1
  • 15
  • 25
1
vote
0 answers

using syn to get the attribute on an enum field of a struct for proc_macro_derive

I am writing a macro for a struct and implementing a method based on the field type. e.g. u8, Array or str. let us say I have this enum represented as u32. #[repr(u32)] #[derive(Debug, Clone, Copy)] pub enum ServerGreetingMode { Unavailable = 0, …
Yousuf Jawwad
  • 3,037
  • 7
  • 33
  • 60
1
vote
1 answer

How can I extract a type from a procedural macro attribute using darling?

I need to be able to extract the type DieselHandler, ideally in a way that allows for repeatable data_loader attributes for transforming against multiple types. #[derive(Loadable)] #[data_loader(handler = DieselHandler)] If I use…
user1363145
  • 107
  • 1
  • 2
  • 6
1
vote
0 answers

Proc Macro with compiler error and quickfix suggestion?

I'm working on some procedural macros in Rust and I use vim-lsp to help me write Rust. I find it very helpful when the compiler error suggests a change to fix an error and I can use a quick action to apply the fix. In my proc macros, I use…
EtTuBrute
  • 502
  • 5
  • 16
1
vote
1 answer

How does syn::parse::ParseBuffer::peek() accept a type as an argument?

How are we able to pass type as an argument instead of turbofish to syn::parse::ParseBuffer::peek? Methods are generally called like input.parse::(). We pass types as generics but peek expects them as input.peek(Token![;]). I wanted to…
Mihir Luthra
  • 6,059
  • 3
  • 14
  • 39
1
vote
0 answers

How do I interpolate function parameter identifiers in a proc_macro using quote?

My goal is to write a simple attribute proc macro, that can be used to annotate a function and print its arguments. example: #[print_arguments] fn add(a:u64, b:u64) -> u64 { a + b } Using quote i can interpolate tokens in scope and with syn…
tungsten
  • 63
  • 1
  • 6
1
vote
1 answer

Unit testing Rust Syn crate

From the Syn documentation: Syn operates on the token representation provided by the proc-macro2 crate from crates.io rather than using the compiler's built in proc-macro crate directly. This enables code using Syn to execute outside of the context…
1
vote
0 answers

How do I debug a proc macro?

So I have a proc macro which is throwing an error. Specifally shader! from vulkano_shaders is throwing: error: proc macro panicked --> src\main.rs:6:5 | 6 | / vulkano_shaders::shader!{ 7 | | ty: "compute", 8 | | src: " 9 …
Jonathan Woollett-light
  • 2,813
  • 5
  • 30
  • 58
1
vote
1 answer

Converting type inside quote! gives trait errors

I get this error: the trait quote::to_tokens::ToTokens is not implemented for proc_macro::Ident when I try to run this code: #[proc_macro_hack] pub fn between(input: TokenStream) -> TokenStream { let idents = input .into_iter() …
NoKey
  • 129
  • 11
  • 32
0
votes
0 answers

rust attribute-like procedural macro failing to parse proc_macro2::TokenStream as expected

Files to regenerate the error base_crate/Cargo.toml [package] name = "add-fields-to-struct" version = "0.1.0" edition = "2021" # See more keys and their definitions at…
Chad
  • 1,434
  • 1
  • 15
  • 30
0
votes
1 answer

How to properly create a call to a function in proc macro with a name depending on the argument in Rust?

I've recently tried to learn more about macros. I've decided to create a macro that will introduce a convenience features in the lib I'm usually working with. My idea depends on two macros, one that is dynamically creating the implementation for the…
snoug
  • 31
  • 1
  • 6
0
votes
2 answers

How do I explictly require a Trait for all arguments of a function in a procedural macro?

The short version The short version is: when writing a procedural macro wrap that wraps around a function (kind of the way the trace crate does), what's the best way to require that all arguments implement a specific trait? The problem trace expands…
Sibear Jo
  • 3
  • 1
0
votes
1 answer

How can I use Derived-macro attribute to store a generic struct in a Vec?

I'm working on implementing dynamic listeners for the communication layer of my embedded program. I want to store a Vec> in my Readers structure. Since T can change, I need to hide it behind a trait: Vec>…
totok
  • 1,436
  • 9
  • 28
0
votes
1 answer

Generating Rust protobuf code with a macro instead of build.rs

There are several Rust crates to support protobufs, but they all require running code generation during the build.rs stage. Is it possible to generate protobuf-serialization code without the extra build step? I see two ways: using Serde-like…
Yuri Astrakhan
  • 8,808
  • 6
  • 63
  • 97
1 2 3
8 9