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.