Templating engine? No thanks.

by Marcel Garus · 2021-11-12 · 2 minute read · this blog · available at mgar.us/no-templating-engine

Here is the Rust code that produces the page you're currently reading (at least at the time of writing). Some time ago, a friend of mine asked me why I used this home-grown templating mechanism instead of a standard like mustache.

Admittedly, this might look very hacky on first impression:

async fn article_full(article: &Article, suggestion: &Article) -> String {
    fs::read_to_string("assets/article-full.html")
        .await
        .unwrap()
        .fill_in_article(&article)
        .replace("{{ suggestion-key }}", &suggestion.key)
        .replace("{{ suggestion-title }}", &suggestion.title)
}

It's just loading the template file and then replacing some strings! (If you wonder about fill_in_article(&article), that also does nothing more than replacing some strings.)

But I still think it's more elegant than using some full-blown templating engine like mustache for two reasons:

  1. It's pure Rust. Depending on one less library means you have to understand one less library. In fact, you can understand this code just by having a rough grasp of the standard library. I'm no Rust magician myself, but I still believe this is the easiest to understand version of the code that can possibly exist, given no prior templating knowledge.

  2. It's type-safe! Instead of having some weird looking syntax like {{#condition}} stuff {{/condition}} in the HTML code, I instead take advantage of Rust's full type safety! Just take a look at how the main page is constructed:

    pub async fn blog_page(articles: Vec<Article>) -> String {
        let mut teasers = vec![];
        for article in articles {
            teasers.push(article_teaser(&article).await);
        }
        page(
            "Blog",
            &metadata(...),
            &itertools::join(teasers, "\n"),
        )
        .await
    }
    

That's just marvelous! It takes a list of Articles, then turns each of them into some small HTML snippet using the article_teaser function and then it joins all of them and puts them in the body of a page. I can just apply my full knowledge of Rust's Iterator protocol without doing any weird hacks in HTML.

And it's not just me: Most modern UI frameworks – Flutter, React, Jetpack Compose, SwitftUI – try to move the power of constructing UI into the code itself instead of keeping it in a separate language.

So, the next time someone offers you a templating library, you might want to step back a bit and think: What's the benefit of adding this other abstraction? Is this really making my life easier overall?

Thanks for reading!

If you liked this article, feel free to share it using this shortlink:

By the way, I wrote other articles about this blog. Here's an article I recommend:

Why This Website Has No Dark-Mode Toggle

2021-07-18 · 3 minute read · this blog

Maybe you're wondering why there's no dark-mode toggle on this website.

Short answer: Misaligned incentives between layers of software result in an overall subpar experience. I want to put pressure on the browser offering to control the dark mode of individual websites.

Long answer: I recently streamed music from my laptop to a Bluetooth speaker and wanted to increase the volume. Naturally, I tried increasing the volume on the computer. Nope, it's already at the maximum. The Bluetooth speaker has volume buttons! Gotta try those! But except for the music being interrupted by one of these awful beeps, nothing happens. Oh, I know! Windows has an option to adjust the volume of individual programs that I use pretty frequently. I must have lowered the volume there. But nada. Finally, I checked in the music app itself, and fair enough, there's the volume slider, its knob almost on the left.

What happened here?