Vert.x, Rust, WASM and a dash of Scala
I decided to share the results of a quick PoC (Proof of Concept) I did a while ago. I wanted to see how easy/hard it would be to connect Rust-generated WASM to a Vert.x/JVM backend. Tl;DR: Damn easy.
If you are unfamiliar with WASM then I highly recommend you catch up on the basics fast or live with the following rough TL;DR:
WASM is a byte code specification supported by all major browsers (Opera, Firefox, Chrome, …). It isn’t ment to replace JavaScript/ECMAscript but to fill in the gaps. It’s main properties are:
compact code
easily integrated with JavaScript
highly optimizable
provides access to all browser capabilities
WASM resembles old school assembly (following WASM text format sample generated from the exmplae code using wasm2wat from wabt)
This is something I really don’t want to write by hand but I still want to verify the claims WASM makes.
With this in mind I started to look for options which then lead to the code and text you are about to see.
TL;DR:
If you don’t care about my elaborate writing you are free to jump directly to the demo-project and start poking around.
The PoC
I have somewhat fallen in love with Rust and since it has a fully working WASM-toolchain I decided to implemented the client side with it.
On the server side I will rely on my beloved Vert.x and implement a small Scala based Websocket-server to stream data to the client. There things should be rendered into a fancy chart.
The end result will look like this.
The WASM client
The job of the client is rather simple:
- Connect to a server and open a websocket
- receive values from the websocket
- render values into a chart
Before I got into it I needed to do some preparational work on my dev machine.
WASM development environment
I personally use IntelliJ IDEA community edition with the Rust plugin. I’ve also heard good things about Visual Studio Code and even Vi, so pick whatever works best for you.
The next thing to get is wasm-pack, which is going to handle the task of generating the actual WASM from the Rust code.
Another good addition is wabt, which provides several nice tools to poke around in WASM-code.
WASM debugging
I had to learn the hard way that debugging raw WASM without preparations can be … rather painful. There are a few things to do in your application to improve things quite abit.
First of all: Handle those damned panics.
When working with WASM and Rust you will hit situations where things don’t work as expected, either with a broken API or a missing implementation, or yourself messing up in unforeseeable. These things aren’t visible during compilation but will cause a panic when launched in the browser.
So make sure to get console_error_panic_hook and register it using the following code.
This will produce readable errors instead of a generic Panic-error message and helped me track down where things went south when working with std::time in the browser (more on that below).
The next things was to get console logging going.
I found the following snipped (whoever figures out where this is from, I’d be happy to add an attribution in this article).
This binds the JavaScript console.log API to the console_log macro which in turn allows printing arbitrary messages to the console using Rust-code like this:
console_log!("Connecting to {} and rendering results in plot", ws_url);
That’s it for our preparations.
Rust libraries
The Rust-ecosystem is constantly growing so it isn’t hard to find libraries fulfilling my needs for this PoC.
First of is stdweb, which provides all functionality to use Web-APIs. In my case the only thing needed is websocket connectivity which you get with this:
This piece of code will connect to a socket, register a listener and start receiving messages.
Now we need something to draw the charts. plotters is a great library with tons of charts options and several rendering backends, one of which is WASM. The library comes with good documentation and a lot of helpful examples. Integrating it worked like a charm.
And finally: Solving a nasty surprise.
So, I mentioned above a problem with std::time, which was causing me a lot of trouble until I figured out how to use the panic_hook. Std::time is simply not usable in the browser as of the time of my experiments, so I had to switch to an alternative time library which was working with WASM and I found chrono to deal with the problem.
The whole implementation of receiving asnd rendering things wasn’t too hard and you can check the whole thing here.
Building it
Building the final WASM is another rather simple task.
Using wasm-pack-command with target set to web I create a native ES module directly importable by the browser. The output path is set this way because I was too lazy to set up something more elaborate and I wanted everything to go straight into the content root of my Vert.x-server. This is the full command to be run after checking out the repo and switching to the websocket-plotter-wasm directory:
wasm-pack build --target web --out-dir ../vertx-websocket-server/src/main/resources/webroot/pkg
The Server
I use Vert.x for most of my experiments. It’s a high performance, reactive, compact application framework which allows easy creation of JVM-based backends.
I also happen to be the maintainer of the Scala language integration, which is the reason why I use it for stuff like this.
Implementing the actual websocket server is rather simple and only takes a couple of lines. For this PoC the server has to do two things:
- serve the static content
- manage websockets
The static file server to serve the wasm/js/html … is just three lines of code.
It takes not only care of serving the files but also to provide the correct ocntent headers, like application/wasm for files with .wasm suffix.
The second part is for handling websockets which generate a JSON file containing time property and another one named value containing a random float value.
With this code we end up with a new random value generated every 100 ms.
And that’s it for the whole thing.
Running this server using sbt run and pointing your browser to http://127.0.0.1:8666/static/www/ will display the chart shown above. Open the browser console to check what has been going on and start playing around with it.
The Results
I am still quite the Rust-Noob so implementing the client side wasn’t as easy as I noted above (now you know, valiant reader who actually got to the end of this). But looking at the code things are clear enough to me to have learned quite a bit from it.
Most important findings to me were console-logs and the panic-hook, both improving the dev-experience quite a bit.
Hooking client and server up was a pleasantly boring experience. Same goes for building things.
Thanks for reading.
Links from the article
- the PoC: All the code from the article
- vertx: a reactive, JVM based application framework
- wabt: several tools for working with wasm code (brew install wabt )
- wasm-pack: Tooling for generating WASM from Rust code
- console_error_panic_hook: Have some nicer information in the borwser console on panic
- plotters: Rust-Library for plotting which provides a WASM-backend
- chrono: Replacement for std::time in the browser