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

Can the code generated by a proc_macro be placed at the top level?

I am creating a proc_macro using proc_macro_attribute that creates a test module for the function it annotates. For example, if I have the following function: #[my_proc_macro] fn my_fn() -> i32 { 42 } The macro will create the equivalent to: fn…
Alexrs95
  • 392
  • 1
  • 3
  • 13
2
votes
1 answer

Is there any way to supply generic arguments to a function-like proc macro?

So let's say I have a proc macro defined like this: #[proc_macro] pub fn my_macro(input: TokenStream) -> TokenStream { ... } Is there any way to introduce a generic argument? For instance, I would like to be able to do something like…
sak
  • 2,612
  • 24
  • 55
2
votes
0 answers

Rust proc_macro's: adding constants

I am trying to add a procedural macro that adds constant field to a struct. Example: #[add_const] struct MyStruct { id: u32, name: String } // Expands to: struct MyStruct { id: u32, name: String } impl MyStruct { const FIELDS:…
Jomy
  • 514
  • 6
  • 22
2
votes
0 answers

How can I use `syn::parse::Parse` (or more generally, `ParseBuffer`) from a context where all I have is a `syn::buffer::Cursor`?

I'm working on a proc_macro2/syn macro, and I've been using Cursor to do low-level parsing of something... Rust-like. But deep in my grammar, I need to parse a syn::Type. As far as I know, the only really good way to do this is to use the impl…
2
votes
1 answer

Transforming attributes into identifiers on proc macro derive

I'm writing my first proc macro and despite trying to read through the source for thiserror, structopt and derive_more I can't seem to find exactly what I'm looking for. I want to transform this: #[derive(Attach)] #[attach(foo(SomeType,…
leshow
  • 1,568
  • 2
  • 15
  • 30
2
votes
0 answers

Is it OK to treat the proc-macro crate in a two-crate library as implementation detail and not follow semantic versioning?

Many libraries that contain procedural macros consist of two crates: a proc-macro crate implementing the actual macro and a normal "main" crate that reexports or wraps the proc macro. This is done because proc-macro crates cannot publicly export…
Lukas Kalbertodt
  • 79,749
  • 26
  • 255
  • 305
2
votes
0 answers

How to use a proc macro to create a macro that can violate hygiene?

I am trying to create a simple library called derive_pattern. My ultimate goal is to be able to write something like this: #[derive(Pattern)] struct TestStruct { x: i32, y: i32, } #[test] fn it_works() { let test = TestStruct { x: 5, y:…
Joseph Garvin
  • 20,727
  • 18
  • 94
  • 165
2
votes
1 answer

Can a procedural macro be debugged as a function?

I'm trying to debug a complex procedural macro in a library I am using. Since I cannot use a debugger with macros and various macro expansion tools have proved useless here, I'm looking for an alternative. Can a procedural macro be run like a…
Jonathan Woollett-light
  • 2,813
  • 5
  • 30
  • 58
2
votes
1 answer

How can I generate files from Rust procedural macros?

I am building an extension API for R in Rust. I annotate my functions with a procedural macro to generate C wrappers with the appropriate conversion and error handling: use extendr_api::*; #[export_function] fn hello() -> &'static str { …
Andy Thomason
  • 328
  • 1
  • 9
2
votes
0 answers

How do I write simple unit test functions for attributes when creating procedural macros?

I'm trying to test parsing attributes for structs as I'm beginning to write a custom derive. I would like to split testing custom derives into multiple simpler test functions rather than testing it as a whole derive system at once. This is my…
S.R
  • 2,411
  • 1
  • 22
  • 33
2
votes
1 answer

Getting the target folder from inside a rust proc-macro

As the title suggests, I'm trying to retrieve the target folder to hold some intermediate compilation products. I need the actual target folder as I would like to cache some values between compilations to speed them up significantly (otherwise I…
Davide Mor
  • 83
  • 7
2
votes
1 answer

How can I compute an instance of a type in a function-like procedural macro and return it?

I have the type Foo: pub struct Foo { ... } Now I want to create a procedural macro that creates an instance of this struct. This might involve heavy computation, file access, or other stuff only procedural macros can do, but the exact details of…
Nils André
  • 571
  • 5
  • 17
2
votes
1 answer

How do I use a custom namespaced derive-macro attribute on a type instead of inside the type?

I'd like to create a custom derive macro that uses the new namespaced attribute syntax: example::attr. I've been able to get this to work with attributes within the type (on a struct field or an enum variant, for example), but not when applied to…
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
2
votes
1 answer

Cannot call a function-like procedural macro: cannot be expanded to statements

I'm trying to get my head around function-like procedural macros and struggling with the basics. For a starter, I tried to create a macro that just prints all the tokens and does nothing: extern crate proc_macro; extern crate syn; use…
Amomum
  • 6,217
  • 8
  • 34
  • 62
1
vote
1 answer

Does syn::Variant have a mechanism to specify variant visibility?

I have a procedural macro that generates an enum plus its variants and I'd like to add configurable visibility to it, but it looks like the syn::Variant struct doesn't have a visibility field. For some reason it can parse a variant with a visibility…
A.Z.
  • 318
  • 1
  • 8
1 2 3
8 9