Rust Proc Macros
Fun with Proc Macros
This will be a super short post about some of my thoughts messing around and using proc macros in Rust
What is a Macro
To understand proc macros, it’s important to understand what a macro
is.
Macros are in a way plugins for the compiler. They take tokens and return Rust syntax to replace the tokens. Often times macros are used to avoid boilerplate code and repeating the same code over and over again.
Lets demonstrate with an example!
Lets say you want to implement a trait MyTrait
on three types A
, B
and C
, but all three implementations will be the same.
The following could be MyTrait
:
|
|
Without macros, you would have to do:
|
|
With macros you can define the macro (I’m calling it impl_my_trait
):
|
|
Then call it like the following:
|
|
The above would expand into the exact same repeated code we saw earlier!
I’m glossing over the syntax and how they work, more details on macro syntax in the amazing chapter in the rust book
Proc macros!
Proc macros, short for procedural macros are a form of macros, that are written as Rust programs!
Wait, so why would I use proc macros?
Because normal macros are limited in syntax and you would have to resort to writing recursive macros for anything complicated. Proc macros are rust programs, meaning you can take advantage of the whole rust language!
But… how does that work, I thought macros run before compilation? how do you get a Rust program to run… before the Rust program is compiled 😖
Well, that’s the root of why all proc-macros have to live on their own crate, with proc-macro = true
in the Cargo.toml
!
Writing proc macros
Writing proc macros always starts and ends with TokenStream
s. As the name suggests, those are a stream of Rust Tokens (they don’t need to be a valid Rust program though!)
A proc macro takes a TokenStream
and returns one. The proc macro’s job would be to transform the input token stream into an output one, using Rust.
There are some AMAZING tools that help a lot with that process, almost all are written by dtolnay
🔥 The most useful ones for me so far have been syn
, quote
and proc_macro2
There are multiple types of proc macros, and syntax to invoke them, so I would suggest readers to check out the reference in the Rust book
An example proc macro definition looks like the following, this is taken from an open-source workshop on proc-macros, written by dtolnay
(totally mind boggling how one person can be so impactful to a community, if you ever read this, you are incredible!)
|
|
You can view the workshop on it’s github repository
You can also find my implementation of the first exercise on my fork
Conclusion
That’s all! This was short, and didn’t go into details at all… but hopefully the resources I linked throughout can provide the reader with more information. I’m planning to start writing short posts similar to this in the future with random thoughts 🕺