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
4
votes
2 answers

Rust proc_macro_derive: How do I check if a field is of a primitive type, like boolean?

I'm trying to filter out all the fields of a struct that are of type bool. But the syn::Type enum doesn't seem to have a case for it, or I'm reading the definitions incorrectly: pub enum Type { Array(TypeArray), BareFn(TypeBareFn), …
Typhaon
  • 828
  • 8
  • 27
4
votes
1 answer

Parsing content of brackets in rust proc_macro

I am building a small HTML parser in Rust using syn and proc_macro2. I have made it so far that i can parse regular HTML tags and it's attributes So for example html!(
) Works But i would like to parse some JSX…
Tonis
  • 111
  • 5
4
votes
1 answer

Unable to get field attrs with syn/darling

I am trying to get field attributes in a proc-macro with syn/darling crates. Here is a MRE Cargo.toml [package] name = "darling_attrs" version = "0.1.0" edition = "2018" [lib] proc-macro = true [dependencies] darling = "0.10" proc-macro2 =…
syskov
  • 86
  • 3
4
votes
1 answer

How to get the content of a module passed to an inner attribute macro?

So I have defined the following procedural macro: #[proc_macro_attribute] pub fn hello(attr: TokenStream, item: TokenStream) -> TokenStream { println!("attr: {}", attr); println!("item: {}", item); item } then I apply this proc macro to a…
amlo
  • 105
  • 1
  • 8
4
votes
1 answer

How can I store an identifier (`proc_macro::Ident`) as a constant to avoid repeating it?

I am writing a procedural macro and I need to emit a very long identifier multiple times (potentially because of hygiene, for example). I use quote! to create TokenStreams, but I don't want to repeat the long identifier over and over again! For…
Lukas Kalbertodt
  • 79,749
  • 26
  • 255
  • 305
4
votes
1 answer

In a procedural macro, how can I check if a string is a valid variable name and not a keyword?

In a procedural macro, I wish to be able to check a string is a valid variable name and is not a keyword. proc_macro2::Ident will panic if one tries to use an invalid variable name, but it will allow keywords which I do not want to be allowed. It…
Henry Gomersall
  • 8,434
  • 3
  • 31
  • 54
4
votes
1 answer

Infer the name of the calling crate to populate a doctest in a procedural macro

I'm creating a procedural macro that auto-generates a library from some configuration file (it's a register layout, but that's not important for the question). I would like the library to auto-generate the documentation accompanying the auto-library…
Henry Gomersall
  • 8,434
  • 3
  • 31
  • 54
4
votes
2 answers

Can proc macros determine the target of the invoking compilation?

Procedural macros live in their own crates, which are compiled for the development machine (so that they can be executed when crates that use them are compiled). Any conditional compilation directives within the procedural macro crates will…
eggyal
  • 122,705
  • 18
  • 212
  • 237
3
votes
1 answer

use Rust proc macros to generate dynamically named struct instance methods

I'm trying to write a procedural macro that generates methods for doubling all fields that are f64. I have it working for a single field with ./src/main.rs use attr_macro::DoubleF64; #[derive(DoubleF64)] struct MyStruct { my_string: String, …
Chad
  • 1,434
  • 1
  • 15
  • 30
3
votes
1 answer

Generating paths in submodule context given path in parent module context

I am creating an attribute macro that can be invoked with (a path to) some type amongst its attributes: #[my_macro(path::to::SomeType)] This macro generates a module, within which the specified type is to be used: mod generated { use…
eggyal
  • 122,705
  • 18
  • 212
  • 237
3
votes
2 answers

How do I pass arguments from a generated function to another function in a procedural macro?

I'm trying to create a macro that generates two functions from a function signature. As the signature of the functions is dependent from what is passed to the macro, the number of function arguments is varying. The functions I want to create are (i)…
3
votes
0 answers

Pass an argument of my proc_macro to include_str

I'm trying to pass an argument of my proc_macro as an argument to include_str!, but getting error: argument must be a string literal during the compilation: /* In lib.rs: */ #[proc_macro] pub fn include_rsml(input: TokenStream) -> TokenStream { …
vdudouyt
  • 843
  • 7
  • 14
3
votes
1 answer

Procedural attribute macro to inject code at the beginning of a function

I'm trying to build a simple attribute which will inject a let binding at the beginning of a function, so the result would be: #[foo] fn bar(){ // start injected code let x = 0; // end injected code ... } I've gotten to this: use…
Dominus
  • 808
  • 11
  • 25
3
votes
2 answers

How can I parse a `syn::Signature` from a `syn::parse::ParseStream`?

I am experimenting with Rust procedural macros. I would like to be able to create a macro that would be used to generate JNI invocation boilerplate. Something like jni_method!{com.purplefrog.rust_callable.Widget, fn echo_str(&str)->String} I have…
Mutant Bob
  • 3,121
  • 2
  • 27
  • 52
2
votes
1 answer

error: `cannot find attribute in this scope` when using custom proc_macro with attributes written with darling in rust

I am writing a library which includes a custom derive macro with custom attributes. For this I use darling. My project structure, thus, is as follows: ├── pg-worm │ ├── pg-worm-derive │ │ ├── src/lib.rs │ ├── src/lib.rs My proc macro is…
Einliterflasche
  • 473
  • 6
  • 18
1 2
3
8 9