Procedural Macros
Procedural macros allow you to run custom code during the compilation process. They operate with the same level of access as the compiler itself, including access to standard I/O streams and the file system. Because of this, procedural macros share the same security considerations as Cargo build scripts. However, they are also subject to several unique constraints:
- They must be defined in a dedicated crate type specifically marked as a
proc-macrocrate. - A
proc-macrocrate can only export procedural macros; it cannot export standard functions or types. - Procedural macros cannot be used within the same crate that defines them.
- Defining them requires the compiler-provided
proc-macrocrate, which is only available within these specialized macro crates.
These constraints can make testing and debugging procedural macros challenging, often creating a barrier for developers who are just starting out.
Fortunately, David Tolnay, a prominent figure in the Rust community, has created several essential crates that simplify this workflow. In this chapter, we will focus on three of them: syn, quote, and proc-macro2. These are the standard tools used in nearly all professional Rust projects for building procedural macros.
By using these crates, we can write our macro logic as standard, testable Rust code. This decouples the core logic from the restricted environment of a proc-macro crate, making it much easier to verify and debug using standard tools.
Instead of jumping straight into the complexities of procedural macro crates, we will begin our journey by learning how to write “code that generates code” within a standard Rust environment.
Let’s go!