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

No main function error when parsing the attributes of the proc macro

In a procedural macro attached to a main function, I am parsing the attributes passed as AttributeArgs to the macro. #[proc_macro_attribute] pub fn macro(_meta: CompilerTokenStream, input: CompilerTokenStream) -> CompilerTokenStream { let attrs…
Alex Vergara
  • 1,766
  • 1
  • 10
  • 29
0
votes
1 answer

Pass a constant into a procedural macro

I am making a macro to expand geometric algebra expressions, and in order for the macro to work, it needs to know to know the type of geometric algebra (3D VGA, 2D PGA, etc.) at compile time. I could do this easily by passing the type (3D PGA) in…
0
votes
1 answer

Syn read file and add line to function

I'm trying to parse a file with syn, and add a line to the single function in it. However, it seems to not modify the file at all when writing it back out. I'm fairly sure that I don't understand fully proc-macro and am using it wrong. In my…
Mr.Smithyyy
  • 2,157
  • 12
  • 49
  • 95
0
votes
1 answer

How do I write a custom attribute for const fields in my Rust library?

I'd like to write a custom attribute for a const field, which will later be accessed throughout my entire library. Example // default declaration in `my_lib`... pub const INITIAL_VEC_CAPACITY: usize = 10; //...but can be overriden by dependent…
ImajinDevon
  • 287
  • 1
  • 11
0
votes
1 answer

Value of a field attribute on a proc macro

I have this struct: pub struct Thing { pub some_field: i32, #[my_attr = some_value] pub field_attr: String } How can I recover the data on the right side of the equals? I can recover perfectly fine the left side. pub fn new(name:…
Alex Vergara
  • 1,766
  • 1
  • 10
  • 29
0
votes
0 answers

Get module resolution entry point in proc-macros

My question is: Is there a way to get the exact module resolution entry point in the proc-macro stage? First off some background info on what I'm trying to achieve. I'm in the process of writing a crate that can automatically implement various…
Nukesor
  • 945
  • 1
  • 7
  • 13
0
votes
1 answer

self on a proc_macro

I am writing a procedural macro that takes the fields of a struct and sends them to another method: pub fn my_helper_macro_builder(macro_data: &MacroTokens) -> TokenStream { // Retrieves the fields and tries to add self to take it's value …
Alex Vergara
  • 1,766
  • 1
  • 10
  • 29
0
votes
2 answers

How do I access the token stream of a trait when writing a derive macro for a struct?

I have a struct and a trait: struct Foo { i: i32, } trait FooTrait { fn foo(&self); } I want to create a derive macro for the struct which generates the impl: impl FooTrait for Foo { fn foo(&self) { println!("generated code:…
Dejavu
  • 1,299
  • 14
  • 24
0
votes
1 answer

Syn get flat token stream

For the sake of this example: I want to write a proc macro attribute which modifies all number literals in a function body. I have a syn::Block from a syn::FnItem. I want to map all over the tokens in the block and change literals. I currently…
Ben
  • 3,160
  • 3
  • 17
  • 34
0
votes
1 answer

Passing a data structure as an args to a proc macro attribute

I'm trying to create a macro that to use on a struct, in which you get another struct as an argument, then concatenate the structa fields. This the code of the macro #[proc_macro_attribute] pub fn cat(args: proc_macro::TokenStream, input:…
0
votes
1 answer

In a Rust macro, How to follow a symbol to get the token stream of another function's definition?

A macro-decorated function calls another, how to get the callee function's token stream in the macro? decorated function: #[follow] fn caller(){ return callee(); } callee: fn callee(){ ... // some…
Lin Lee
  • 213
  • 2
  • 8
0
votes
0 answers

How do I filter out all the fields that are of Option type in proc_macro_derive

I'm creating my first derive macro. I'm trying to filter all non optional fields from the struct. I want to use those for the constructor. I'm not sure how I extract them. I was thinking of getting the path of the type, and to check if that path is…
Typhaon
  • 828
  • 8
  • 27
0
votes
0 answers

What is wrong with my or-pattern procedural macro that causes "macro expansion ignores token `|`"?

Consider the following example code: #[derive(Clone, Copy)] #[allow(dead_code)] enum Enum { A { a: u8, b: u8 }, B { a: u8 }, C { b: u8 }, } fn foo(e: Enum) -> Option { match e { Enum::A { a, .. } | Enum::B { a, .. } =>…
Turakar
  • 180
  • 2
  • 13
0
votes
1 answer

How can a procedural macro check a generic type for Option> and flatten it down to a single Option?

I'm writing a derive procedural macro where all of the values are converted to Options. The problem is that any Option fields in the struct can be contained within these Option types. On its own, this isn't much of an issue until I start to…
Abeltensor
  • 132
  • 1
  • 10
0
votes
1 answer

proc_macro_attribute seems to not play nicely with struct impls and traits

I'm trying to get a procedural macro to print out information about variables in my function inputs. When I test the macro in a separate crate, I get an error from the compiler that I'm not implementing the trait functions even though the trait…
1 2 3
8
9