Wilxoft

Templates and Rust

This post is about what I would like from a template library in Rust. Rust is self described as...

"Rust is a systems programming language that run blazingly fast, prevents segfauts, and guarantees thread safety"

I'm somewhat obsessed with Rust because it allows me to take control over memory but it also prevents me from doing some pretty stupid things. If you don't know much about Rust go check it out at https://www.rust-lang.org.

One of the things I like most about Rust is it's commitment to safety, specifically compile time safety. I like it so much so that I want my templates for this blog and other sites to be compile time safe. However, compile time safety means I have to re-compile my site every time I change anything in a template. This for me and for others can be a deal breaker as it doesn't allow for easy experimentation in a template. The feedback loop is to long and the benefit of compile time safety is next to nothing when in the experimentation phase.

As far as I know there are three options to get around this poor development experience of compile time templates. Option one is to not use compile time templates. I don't really find this appealing as then I lose out on the safety of compile time checked templates. Option two is to develop your templates as static HTML files until you have what you want, and then code them into your application. Although This option is a little better in my opinion it still lacks a good workflow, and requires extra work when you are done experimenting. This option also doesn't let you experiment within your normal environment. Option three is my favored option, using a single template library that can do both compile time and run time templates.

Implementing Option Three

The first steps

Let's talk about the first step of implementing such a library. The first thing I would want is to be able to have my templates live in normal HTML files. So my first question. How do you get normal HTML files to be compiled into a rust program? You use include_str!.

let template = include_str!("html/index.html");

Of course now you are going to tell me that I still don't have any of the safety guarantees of compile time templates. I agree. But what this does is loads up the contents of the HTML file and now we could check the template at compile time by building a macro around this that would create the template. Well now my system is looking like just compile time templates. This is true. However we can get over this as well.

In rust you can use features to do conditional compilation. For our case it allows us to have a compilation option to compile templates or to not. So this means when we compile with the compile_templates feature we will use the code above to get the template into the source and compile it. When the compile_templates feature is turned off we load the template file directly from the file system during run time. It will look something like this, but a real library would compile the template when using the compile_templates feature instead of just including it.

#[cfg(feature = "compile_templates")]
fn get_index_template() -> Result<String, Error>{
    Ok(include_str!("html/index.html").to_owned())
}

#[cfg(not(feature = "compile_templates"))]
fn get_index_template() -> Result<String, Error>{
    // this should probably be a config option
    let mut file = File::open("../templatelib/src/html/index.html")?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;
    Ok(contents)
}

We will also have to turn the feature on or off when compiling our binary. To do this I use Rust's package manager cargo. In the Cargo.toml file for the binary the import of the library that contains the compile_templates feature will look something like this.

# for run time templates
templatelib = { path = "../templatelib"}
# for compile time templates
templatelib = { path = "../templatelib", features = ["compile_templates"]}

Hey, now we can compile or not compile our templates into our binary. Of course there is a lot more work to do. We still need to make our templates actually checked at compile time. We still need to implement all of the features that a normal template library would have such as variable replacements, loops, components etc. I think the basics for what I want in a rust template library are entirely possible, and even a good idea. I would love to hear your feedback or maybe you are working on something similar or have better ideas. We can discuss them on https://www.reddit.com/r/rust/ or https://users.rust-lang.org