Latest news about Bitcoin and all cryptocurrencies. Your daily crypto news habit.
Iâve been working on a project called Thruster recently, and needed a way that a developer could reasonably use templates. Thruster is a middleware based web server written in Rust (get it, th-rust-er? Iâm working on my tight 5 for amateur night at The Apollo,) and as such, I needed a way to load HTML templates and insert variables into them in a performant way. Rather than poking around the numerous existing libraries and choosing one made by someone I donât knowâââstranger danger!âââI decided to make it myself. This article is about that journey, the unbelievably thrilling adventures of writing my first proc_macro_derive in Rust. Cue the Indiana Jones music.
The first thing you need to know, is that Iâm not what Iâd call a systems guy. I picked up rust because I wanted a newer language that was performance based, but Iâm really more of a web developer. I can already hear your salt through the internet tubes already; âWhy in the world would I take advice from a web developer? He probably just wants to talk about arrow functions and how his generation discovered functional programming.â Well youâre right. I do just want to talk about that, but Iâm also in the middle of making avocado toast and my life coach said that I should concentrate on my chi instead of talking about Elm-lang.
The reason I mention that Iâm a web developer, is that one of the first things I look for in a web framework is how complicated developing in the framework is and how easy a framework is to compose and move portions around. Sometimes I like to be able to just copy/pasta code Iâve already written in different projects without having to recalculate the trajectory to put a man on the moon.
The second thing you should know, is that Rust is great, and although my coworkers want to throw a shoe at my head if I even look like Iâm about to talk about itâs memory safety or blazing speed, my love for it continues to burn brighter than a thousand suns.
Great, now that the confessional is out of the way, we can actually dig into the meat of the project; proc_macro_derive . Procedural macros (and macros in general,) are fascinating creatures. Many developers from other languages will roll their eyes and remember every time they had to debug a mysterious macro, but Rust is a little different. From the docs:
âŠRust has a hygienic macro system. Each macro expansion happens in a distinct âsyntax contextâ, and each variable is tagged with the syntax context where it was introduced.
Hereâs what I wanted to accomplish in for my macro:
- It should draw in a template from a file during compilation rather than at runtime.
- It is type safe, i.e. I wonât end up with a panic! or some strange undefined type text in my rendered template during runtime.
- It is fast.
The first bullet point there, unfortunately, discounts just regular old macros, so I had to start digging into procedural macros. Macros that essentially have extra logic that can be brought in during the compilation process and actually alter the AST of the code.
Procedural Macros are special beasts, so to start, I needed to declare that my code is a special snowflake in the Cargo.toml file:
[lib]proc-macro = true
I also included a few handy dandy packages for going from human code (oxymoron?) to a rust AST and back again, syn and quote:
syn = "0.13.1"quote = "0.5.1"
Next I started the code for the actual macro. An important distinction here to make was the difference between the different types of procedural macros. Iâll just leave this here because someone already did the work for me in the Rust Dark Arts book: The Unstable Book.
Long story shortâââI decided to make a custom derive procedural macro! That means my code looked like this to start:
At this point I had a way to add custom implementations to a struct, exactly what you would want to use a derive for. Thatâs all well and good, but I still wanted to be able to load a template from a file. The question is, how in the world to get the name of the template to load?
Well, luckily for us, weâre in compiler land (much worse than Candy Land) so we can add extra attributes as well. Hereâs the code with the added TemplateName attribute for grabbing the template
Yes, I know the top is a bit of a mess. Like my garage and my desk at work, Iâll get to cleaning it up when I have some free time. What Iâm doing here is iterating through the attributes on the struct thatâs passed in in order to find the TemplateName attribute. Then, Iâm loading a fileâs contents based on that string, and passing it into our token stream made by quote!.
The last step is doing the actual interpolation, which gets a little messy, but is relatively straightforward to do. The important thing to remember is that everything outside of the passed back contents from quote! will be done at compile time, and vice versa. Note that the example above doesnât actually include interpolation, instead, hereâs a link to the published packageâs source on GitHub.
Youâve done it. Youâve followed my scattered logic and forced rhetoric to the final scene where the faces melt off of the Nazis (you didnât think Iâd forgotten about the Indiana Jones reference in the first paragraph did you?) Procedural Macros are great in Rust; theyâre type safe and hygienic, but theyâre not a silver bullet. They have limits to their power for the safety of the developer, and the sanity of anyone who has to maintain your code. If there is a problem thatâs decidable at compile time, however, theyâre a pretty great tool.
You can find the code here: https://github.com/trezm/fuel-line and appropriate cargo packages here: https://crates.io/crates/fuel_line and here: https://crates.io/crates/fuel_line_derive.
Fast and Simple Rendering in Rust using Proc Macros was originally published in Hacker Noon on Medium, where people are continuing the conversation by highlighting and responding to this story.
Disclaimer
The views and opinions expressed in this article are solely those of the authors and do not reflect the views of Bitcoin Insider. Every investment and trading move involves risk - this is especially true for cryptocurrencies given their volatility. We strongly advise our readers to conduct their own research when making a decision.