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
0
votes
0 answers

Procedural macro in Rust to append arms to an existing enum

I am new to Rust + procedural macro's — apologies because I don't know what I am doing Background: I am using the rustdds crate to implement DDS. I have created extensive wrapper code to customize these domain participants to talk on custom topics…
0
votes
1 answer

A procedural macro that could count the occurrences of a particular enum variant in a collection

I'm trying to make a procedural macro (CountOf) that counts the ocurrences of a particular enum variant in a collection, in this case a vector. This src/macros.rs is what I'm using for testing the macro #[derive(count_of::CountOf, Clone)] pub enum…
0
votes
1 answer

Get proc macro caller location in the proc macro

I have a proc macro that needs to read files relative to the caller location: // main_crate/src/lib.rd use other_crate::my_proc_macro; my_proc_macro!(file1, file2) This would mean the proc macto needs to read main_crate/some_dir/file1.txt and…
nxe
  • 241
  • 2
  • 9
0
votes
1 answer

Proc macro "main" not expanded + Rust-analyzer not spawning server

I join these two questions in one, as they maybe are related. A few days ago, I started having this error in the [#actix_rt::main] line, before the main function: proc macro `main` not expanded: cannot find proc-macro server in sysroot…
Zerok
  • 1,323
  • 1
  • 24
  • 56
0
votes
1 answer

Bypass field visibility requirements when using proc-macro to structurally construct types

Is there a way to bypass some field visibility requirements within code generated by proc-macros to structural construct types? For example, lets say that I have this struct and I use a proc-macro to construct it with some external data. To be…
Locke
  • 7,626
  • 2
  • 21
  • 41
0
votes
0 answers

How to check for mutability in syn's `PatType`?

I have gotten a PatType from an FnArg::Typed(my_pat_type) which I got by extracting the inputs from a function signature in a procedural macro. My problem is that I need a concise way to check if my PatType is in any way mutable or not, without…
whyrgola
  • 11
  • 1
  • 4
0
votes
0 answers

use-after-free of `proc_macro` symbol

I got this error when I executed a macro created by proc-macro, my error : error: proc macro panicked --> src/main.rs:16:5 | 16 | load_extension_parser!(B); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: message: use-after-free of…
NightProg
  • 1
  • 1
  • 3
0
votes
3 answers

how to debug a custom proc macro?

I am studying Rust macro, and i want to see the expand output of the macro. I had tried by using command: rustc +nightly -Zunpretty=expanded xxx.rs follow the tutorial The Book of Rust Macros - Debugging. but i got the error when i use the custom…
Dan
  • 1
  • 1
0
votes
1 answer

Why is this procedural macro panicing

I am using proc macros to parse a given input into a Node tree, for debugging purposes I want to stringify and print the output to see if I am successfully converting to RPN, my current function: use proc_macro::*; #[proc_macro] pub fn…
0
votes
1 answer

Having trouble getting started with proc macros (why doesn't this work?)

I'm trying to follow this post to get my feet wet with proc macros (Can a procedural macro be debugged as a function?) I came up with this to just to get a feel for how it all works: extern crate proc_macro; use proc_macro2::TokenStream; use…
Andrew Voelkel
  • 513
  • 4
  • 10
0
votes
0 answers

Rust Procedural Macro - macro expansion ignores token `,`

I have the following trivial procedural macro in one crate (it was more than this, but I distilled it to be as simple as possible while still causing the issue to occur) #[proc_macro] pub fn the_macro(input: TokenStream) -> TokenStream { …
me163
  • 1
0
votes
0 answers

Is there a way to get the original source, including whitespace, from inside a proc macro?

I'm working on a DSL implemented as a proc macro, and would like to be able to get to the original source, as a string, of the contents of the proc macro. So for instance, if my proc macro is invoked like so: my_macro! { one two three } I would…
sak
  • 2,612
  • 24
  • 55
0
votes
0 answers

Is there a pattern to change compile behavior based on a type (proc macros)

I am trying to write a procedural macro that performs different behavior based on a trait type, which the macro may not have access to. For example, given the following trait: trait Bar { type BarType; } I would like to be able to change macro…
Trevor Gross
  • 454
  • 3
  • 11
0
votes
1 answer

How to disable the "unused code must be used" warning from macro?

I tried to add allow dead_code and unused_must_use: #[allow(dead_code)] #[allow(unused_must_use)] #[implement(MyStruct)] pub struct MyStructList(pub Rc>); But still got the warning, still new to rust, what does it mean to call drop…
Jason
  • 1
  • 1
0
votes
1 answer

How do I extract information about the type in a derive macro?

I am implementing a derive macro to reduce the amount of boilerplate I have to write for similar types. I want the macro to operate on structs which have the following format: #[derive(MyTrait)] struct SomeStruct { records: HashMap
sak
  • 2,612
  • 24
  • 55
1 2 3
8 9