Procedural macros

Declarative macros can become tedious to read and maintain when your code generation logic becomes complex, as you need to write your logic with its own DSL to manipulate tokens. There are better, more flexible ways than using macro_rules!. For complex problems, you can leverage procedural macros as they are better suited to writing something non-trivial. They are suitable for cases where you need full control of code generation.

These macros are implemented as functions. These functions receive the macro input as a TokenStream type and return the generated code as a TokenStream after undergoing any transformation at compile time. To mark a function as a procedural macro, we need to annotate it with the #[proc_macro] attribute. At the time of writing this book, procedural macros come in three forms, which are categorized by how they are invoked:

  • Function-like procedural macros: These use #[proc_macro] attribute on functions. The lazy_static! macro from the lazy_static crate uses function-like macros.
  • Attribute-like procedural macros: These use #[proc_macro_attribute] attribute on functions. The #[wasm-bindgen] attribute in the wasm-bindgen crate uses this form of macro.
  • Derive procedural macros: These use #[proc_macro_derive]. These are the most frequently implemented macros in the majority of Rust crates, such as serde. They are also known as derive macros or macros 1.1 due to the name of the RFC that introduced them.

At the time of writing this book, the procedural macro API is very limited on what can be done with a TokenStream, so we need to use third-party crates such as syn and quote to parse the input as a Rust code data structure, which can then be analyzed according to your needs for code generation. Also, procedural macros need to be created as a separate crate with the special crate attribute of proc-macro = true, which is specified in Cargo.toml. To use the macro, we can depend on the macro in the same way as other crates by specifying it under dependencies in Cargo.toml and importing the macro with use statements.

Among all three forms, derive macros are the most widely used form of procedural macros. We'll take a deep dive into them next.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset