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

Is there a way to emit a compile error if a struct contains padding?

I'm really after an opt-in derivable trait that safely returns an objects unique representation as bytes. In my application, I noticed a ~20x speedup by hashing as a byte array over the derived implementation. AFAIK, this is safe for Copy types with…
kalhartt
  • 3,999
  • 20
  • 25
1
vote
1 answer

Inverse `?` operator in Rust (aka return if Some/Ok)

I am building a macro parser with syn and need to check if a ParseStream is one of several keywords. The code currently looks similar to this: mod kw { syn::custom_keyword!(a); syn::custom_keyword!(b); syn::custom_keyword!(c); } enum…
Gabriel Pizarro
  • 400
  • 4
  • 15
1
vote
1 answer

Is there a way to assert at compile time if all the fields in a rust struct exist in another struct?

Lets say I have a struct struct User { id: u32, first_name: String, last_name: String } I want to be able to make a struct that is only allowed to have fields from a "parent" struct for example #[derive(MyMacro(User))] struct UserData1 { //…
1
vote
1 answer

What is proc_macro2 vs. proc-macro2?

Trying to learn procedural macros as part of internalizing Rust. I don't need competing crates. What is going on here? proc-macro2: https://crates.io/crates/proc-macro2 proc_macro2: https://docs.rs/proc-macro2/latest/proc_macro2/ WTH is going on…
Andrew Voelkel
  • 513
  • 4
  • 10
1
vote
0 answers

Compile-time verified restricted serde subset derive macro?

I'm trying to make a library that serializes things using serde but I'd like better type-safety. Specifically: the format is much more restrictive than serde and I'd like field types incompatible with my format to be rejected at compile-time. I'd…
Indoood
  • 11
  • 1
1
vote
1 answer

How can I create a proc_macro2 span programmatically?

I am working on a procedural macro which implements a small DSL. Currently I am attempting to implement diagnostics-based error reporting, so that I can provide high quality feedback in the IDE setting. My overall approach is to use the Diagnostic…
sak
  • 2,612
  • 24
  • 55
1
vote
0 answers

How do I use rust quote library for a procedural macro that generates a new struct type

I have a struct like this: struct Input { key1: u32, key2: String, value1: u16, value2: u8 } I want to write a proc macro using quote that generates this struct: struct InputKey { key1: u32, key2: String, } How do I do…
user855
  • 19,048
  • 38
  • 98
  • 162
1
vote
0 answers

proc_macro_error::emit_warning does not emit warning at compile time

I'm implementing a simple attribute proc macro that's supposed to throw a warning if an invalid option is provided as an argument to it. Assume the following code: #[proc_macro_error::proc_macro_error] #[proc_macro_attribute] pub fn…
abcalphabet
  • 1,158
  • 3
  • 16
  • 31
1
vote
1 answer

Accept multiple values on proc macro attribute

I wanted to be able to retrieve the content from an attribute like this: #[foreign_key(table = "some_table", column = "some_column")] This is how I am trying: impl TryFrom<&&Attribute> for EntityFieldAnnotation { type Error = syn::Error; fn…
Alex Vergara
  • 1,766
  • 1
  • 10
  • 29
1
vote
1 answer

How to parse other attributes in custom rust proc_macro attribute?

I am writing a proc_macro attribute that add fields to a struct and also implement my trait (and other by adding a #[derive(...)]) to the expanded struct Here a simplified version of what I want to do: #[foo("some value")] #[derive(Debug)] struct A…
Jil
  • 45
  • 1
  • 6
1
vote
2 answers

Create a vector with macro iterator syntax

How can I create a new vector with the iterator Rust macro syntax? I am trying this: unsafe { MUT_STATIC_VAR = vec![ #(#my_outher_vector_data)*, ]; } Full explanation: I am trying to reasign data that I write in one mut static var…
Alex Vergara
  • 1,766
  • 1
  • 10
  • 29
1
vote
2 answers

How to modify all string literals with a proc_macro_attribute in Rust?

I'm toying around with Rust's procedural macros, and I wanted to make a custom funny attribute that turns all string literals in it's TokenStream into SHOUTING CASE. Here's what I have so far in my proc-macro lib called amplify: // lib.rs extern…
camercu
  • 147
  • 7
1
vote
0 answers

How to check if a syn::Field is a struct in rust proc_macro

I have a proc_macro that produces a telemetry function to parse a structs member variables and it works great for non nested structs. I have found that I need to recursively call my handle_named_field function on any syn::Field that is a struct. …
1
vote
1 answer

How to parse nested struct member variables in rust proc_macro?

I am attempting to write a macro that will generate a telemetry function for any struct by using #[derive(telemetry)] . This function will send a data stream to anything provided that is io::Writable. This data stream will be "self describing"…
1
vote
1 answer

Lookahead for "=>" token with crate syn

I'm working on a little domain-specific language with proc-macros and syn, and there I want to parse a sequence of operators on the form lhs + rsh => res lhs - rsh => res or lhs += rsh lhs -= rsh and such. I'm doing this using the syn crate,…
Thomas Mailund
  • 1,674
  • 10
  • 16
1 2 3
8 9