Rust on the Frontend and Backend
An interview with Martin Kavík, creator of the MoonZoon full-stack framework
Backend languages in the browser have been a thing for a long time. Google Web Toolkit would compile Java applications into JavaScript, and I believe (though I may be misremembering this1) that .NET had a feature where you could essentially code as if you were writing a form application in C# and it would compile to a web form and wire up JavaScript, HTML and CSS so you never had to learn anything about web technologies.
Things changed in a couple ways when Mozilla created asm.js (leading later to WebAssembly). Along with Emscripten, you could now compile C and C++ into a subset of JavaScript that executed very efficiently and provided the kind of linear memory model that C backends expect. Generated JavaScript can have pretty bad performance characteristics, so being able to take advantage of decades of research in optimizing compilers for C and C++ in the browser was a big step forward.
Rust uses LLVM, which Emscripten compiles into WebAssembly, so pretty early after the creation of WebAssembly, people were porting Rust to work in the browser. And very shortly after that, there were Rust frontend frameworks.
What’s the difference between Rust frontend code and say, Flash, which was a compiled binary being executed in the browser? Aside from the plugin not shipping with the browser, the main difference is that Rust on the web is designed to integrate with the other APIs in the browser. Specifically, the DOM. Previous generations of frameworks tried to paper over the web and pretend it was just like any other GUI application.
While frontend frameworks are taking web technologies seriously now, it’s also the case that WebAssembly is the result of the web2 taking languages other than JavaScript seriously. I’m personally a fan of TypeScript in the category of “JavaScript but with a little extra”, but if you’re already stuck with a compilation step, why be so concerned with whether the input language resembles JavaScript?3
Rust has a reputation of giving low level performance with high level language features. Does that translate into a big advantage on the web? Yeah, there are some performance benefits, but it’s important not to overstate them. The biggest costs on the frontend are less in “how many loop iterations can I do per second” and more like “how do I avoid reflows and batch my DOM updates”. This varies depending on what kind of application you’re writing, but just like on the backend where we don’t worry too much about language overhead if you’re IO bound, running a more efficient language on the frontend doesn’t buy you much if all your time is spent blocking on the DOM.
Frameworks mitigate much of this on the web. They take care of batching your DOM updates and calculating the minimal set of event listeners that need to be inserted, etc. I’ll write up a better post on these things later, but the bleeding edge of these techniques was pioneered by Svelte, which requires a compilation step. We now have tech similar to this in Rust as well with the dominator library, which MoonZoon utilizes in its frontend to get performance.
Interview with Martin Kavík
I spoke with Martin Kavík, the current maintainer of the Seed frontend framework and creator of a new full-stack framework called MoonZoon. We talk about how he chose Rust, becoming the maintainer of the Seed framework and some of the architecture and design decisions he made in MoonZoon.
The following interview was edited for length and clarity, and incorporates some followup questions I asked him.
Can you talk a bit about how you got into Rust development?
Yeah, I was working in some agencies in Prague in the Czech Republic and I was working with PHP and C/C++ and so on. And it was kind of dangerous. When our app got deployed to production, we’d very often find some problems that were very surprising bugs because of missing language features.
So I was trying to find a better language to eliminate these kinds of bugs, and I tried to learn Elm, Elixir, Go and so on. But I just didn’t like these languages. There were some very clever and nice things, but they were very dogmatic or too universal or designed for beginners (for example, Go). But then I found Rust, and that was it.
It’s the best language (I think), so I wanted to write in Rust and not use any other languages.
You’re the primary maintainer of Seed, how did you get involved with the project?
I was a frontend developer for some years, but there was only JavaScript there, so I moved to the backend. But after years of working in agencies and banks and similar companies, I wanted to move back to frontend because I was a bit tired of devops and microservices.
I was trying to find something like Elm, but in Rust. I tried a new framework, Yew4 and some similar ones, but they felt a little too hard to learn, a little too cumbersome, I didn’t like the API or they were missing documentation. Then I found Seed, and I didn’t really like the code, but the API was very good.
So, I wrote some pull requests to Seed, and the owner was pretty happy to merge them. That was my first Rust contribution to both GitHub and to open source in Rust. Then I saw the opportunity to rewrite and improve the codebase and API because the previous maintainer made me a provisional contributor. He was very good at designing the APIs because of their simplicity.
What led you to developing MoonZoon?
After maybe two years of development on Seed, I was able to write Rust pretty confidently. I had an idea that some of the problems with JavaScript architectures in Rust aren’t really solvable without many breaking changes in Seed’s users’ applications.
So, I had two options. The first one was to refactor and introduce many changes in Seed’s codebase and API. Or I could write another framework (or proof of concept at least) of something better, with a newer and more Rusty architecture without Elm’s influence. That was the motivation for Zoon, MoonZoon’s frontend part.
The second problem was I received many questions about frontend and backend integrations on Seed’s chat, and I wasn’t really able to answer them because there are too many frameworks and architectures on the backend side. Seed, or any other frontend frameworks, have to be flexible enough to support all of these frameworks.
So, I was thinking about how to resolve it once and for all, and the idea of one backend and one frontend was born. MoonZoon is solving the problem of integration, basically. The idea is the developer writes the application in MoonZoon, and it just works because it’s optimized for this small scope where you write the frontend and backend at once.
I was thinking about something like Apple’s technologies where they design the hardware and software, so they can optimize it much better than, for example, Windows where they have to deal with many kinds of hardware. That’s the basic idea.
Has using Rust caused you difficulties specifically?
Rust is a hard language to learn, and that has some advantages and disadvantages. The advantage is that it automatically filters, uh…, script kiddies, basically. So if you want to hire someone to write Rust for you, it’s pretty safe. They’ll already know how to program because it’s hard.
For particular Rust features, I was fighting for months with standard things like the borrow checker and lifetimes. And then you discover some feature with traits or some new features in the language and it’s kind of like relearning the language. So you have to learn constantly.
For example, when the async/.await syntax was new, we had to rewrite the Seed codebase, especially for the fetch API. We ran into some weird problems with the WASM file being too big because we added some dependencies like standard url. That dependency was too big, so we had to remove it and basically rewrite it from scratch. Problems or bugs, not particular to the language, but in dependencies and the ecosystem are the most painful things to resolve.
Most questions from Seed users or bugs in Seed apps are not due to the language itself; something like null
or undefined
. They’re just from semantic errors: they just can’t make it compile.
I think Rust has pretty different problems from JavaScript or PHP. So, you explain and they take this into account, that we will have to rely on the compiler. It’s new thinking for many many people from other languages and backgrounds.
You mentioned earlier that you just wanted to code in Rust from now on, do you mean literally, or do you think there are some places where you would still use another language over Rust?
Maybe there are some places where other languages will be a little bit better, but there are two problems for me. The first one is that my brain is comfortable with Rust. I don’t try to do the compiler’s work in my head. So, for example, when I was working for some months in a company where they were using Go. I would have everything running and working, and I would receive code review comments about something like missing references, or “This will maybe work, or maybe won’t work because of X”.
Rust eliminates these kinds of bugs with its features. In other languages there isn’t this safety net, so I had problems writing in these languages. Afterwards, I would like to write everything in Rust, because it’s basically a systems language for very low-level stuff, but we’re also able to write very nice abstractions with it. And you can compile it to native or anything else.
So, Rust everywhere for me.
What features are you personally most excited about adding to MoonZoon?
I’m writing the “Zoon” part of MoonZoon, so I ended up writing a JS benchmark example app for Zoon5. I’m already seeing the differences between Seed and Zoon, for example, displaying thousands of lines on the screen. When you click on one row, it changes basically immediately in the Zoon app, but in the Seed app you need to wait for it to render the entire page. There are some architectural features paying off that I’ve already seen, so that’s pretty exciting for me. That’s maybe a few months away.
The “Moon” part, the backend, that’s basically a new world for me because I was working on Seed for the last couple of years. I haven’t been able to find another Rust virtual actor framework6. There are some frameworks with virtual actors in Java and C#, but that’s something that’s missing in Rust. So it’ll be from scratch and I’ll be reading white papers and trying to implement it. The goal is to make it scalable and make it work without a database. So I’m excited because it will allow me to write web applications without Postgres and migrations and DB-ops and similar stuff.
Can you give a quick summary of how virtual actors work?
Actor Model: An actor is an object that interacts with the world and other actors using asynchronous messages.
Standard Actors: the actor has to be explicitly created and removed by a developer or by the actor's parent (aka supervisor) by predefined rules.
Virtual Actors: A runtime system manages actors. It creates, removes or moves actors between servers. All actors are always active and cannot fail from the developer point of view. In the case of hardware failure, the lost actors are automatically recreated on another server and messages forwarded to them.
That’s interesting, so in other words it will pull the database into the framework itself so it’s not talking to an external service?
When you write a small application with a backend, you’ll be able to use virtual actors. But I know many apps will require some communication with some custom APIs (microservices or cloud APIs, something like that). So there will be some libraries to communicate with classic, standard APIs, but if you don’t need it you just use virtual actors and you’ll have scalability and basic stuff with authentication built in. It should make your development much faster and less complicated. Less setup for your local machine, and production should be less painful.
Are there any projects in the Rust ecosystem that you think are really interesting but maybe haven’t gotten enough attention?
The first one is trunk. It’s like WebPack for Rust applications. The maintainer is a Seed user, so we know each other from the chat. I’ve already used trunk in a production app for my client, so it’s already working and usable with Seed and Yew (and probably other frameworks).
futures-signals
is super nice and is basically the core of Zoon nowAnother is whatlang, an application built in Seed that uses natural language detection.
Finally, fst is pretty breathtaking. I was working on a frontend for a client in Rust, and the core was based on this library. I was able to search through maybe 20,000 movies by titles in a few milliseconds. When I was comparing it with some JavaScript libraries, they were taking maybe tens of milliseconds. So it’s a very nice example where Rust and WebAssembly is much faster and allows you to change your frontend architecture. You can move your search from the backend to the frontend side and save some money!
Updates & Housekeeping
If you have a paid subscription and haven’t gotten your Always Bet on Rust sticker yet, fear not, I’m sending the rest out this week.
Also, if you know someone who works on an interesting Rust project you think would make a good post please let me know. I have a pipeline of new interviews, but the suggestions I’ve gotten so far have been great.
If someone can tell me what the heck I’m remembering here, please ping me
Who is “the web”? Uh, the complex interacting system of standards bodies, web developers and browser makers that somehow form distributed consensus on which tech should exist, is implemented, and is well supported. Plus Brendan Eich. Ok there, I did it.
There are actually benefits to this approach, which is that browser JITs are very optimized for JavaScript and JavaScript-like patterns. Additionally, you get a high quality garbage collector for free, which WebAssembly doesn’t give you.
The benchmark demo is pretty fun to play around with
Rust has several actor frameworks like Actix and Bastion, but virtual actors are a specific type of actor framework described in this whitepaper by Microsoft.
Yew is another popular frontend framework for Rust
For 1, you are probably thinking of ASP.net Web Forms though there's also ScriptSharp to transpile C# code into JS.