Web Assembly (WASM) and Rust have been growing and evolving over the last couple of years, so what’s it like to use them together?
Introduction
I’ve wanted to use Rust and WASM for a while due to a number of reasons: small bundle size, low-level access with reliable performance, and all the perks that come with Rust (strong type safety, zero-cost abstractions, etc.). So, when I was presented with the opportunity of 2 weeks off-project learning, there was no excuse not to give Rust and WASM a try!
What followed over the next 2 weeks or so was a bit of a programming rollercoaster for me, something all programmers have been through many times. But when it came time to write my experiences up for this article, I noticed there was a pattern, this experience wasn’t just any rollercoaster…it mapped perfectly to the structure of a Romcom! So, to explain and analyse this not officially supported pairing of a web application bundler and a systems programming language, we will be following the standard 10 part Romcom format, for structure and a bit of comedic relief.
Part 1: “An Unfulfilled Desire”
Another reason I wanted to use Rust and WASM is because it was new and shiny, plus it would be nice to hook up the Rust program to a nice web interface. One problem, Rust and WASM is only officially supported with Webpack as a bundler. To me, Webpack was that Ex in a Romcom, it was never a good relationship and we could never make it work. But, it seemed that it may be a necessary evil to reach my goal of making something using my lost love, Rust.
Part 2: “Meet cute”
So, I grudgingly start to clone the Rust WASM Webpack template as I’m flashed back to a previous project, watching myself as I battle with Webpack trying to compile a Single Page App (SPA). The list of dependencies growing with every plugin. I spam Ctrl + C, “No there must be something else” I think. And that's when it hits me, “Parcel! I remember it saying something about WASM?” I think as I quickly navigate to the Parcel website, and there it is, this is what I have been looking for, and after a quick npm install
, I’m head over heels.
Part 3: “Happy Together”
One npm init
and npm install -D parcel-bundler
later and we are off to the races. Parcel supports importing .rs files in JS and TS out of the box so in a simple HTML5 boilerplate with a main.js I do just that. The rust file contains a simple function which when given 2 numbers returns their sum, some extra Rust to tell the compiler not to mangle the function name and it’s done! The JS calls this function and displays the output in the DOM, a simple example but this seems to have everything I need…
Part 4: “Obstacles Arise”
But, as with most romcoms, a bump in the road pulls the relationship into question. For Rust and Parcel, this issue was returning or accepting strings in functions. No matter what I did, it wouldn’t work and streams of undefined would plague my console. Although there seemed to be a solution, the well supported “wasm_bindgen” package provides a polyfill for things missing between Rust and JS! So, make a Rust project with a cargo.toml, add the wasm_bingen crate, import and run… oh wait. Parcel doesn’t seem to work with wasm_bindgen even with a plugin someone on a GitHub issue cites as the solution...what now?
Part 5: “The Journey”
After a few minutes of frantic Googling and skim reading GitHub issues and various tutorials on blogs I find an alternative package, stdweb. Seems to have most of the functionality of wasm_bindgen and a handy tutorial with how to set it up with Parcel! A quick switcheroo of the packages in the cargo.toml, some slight code tweaks and we are back on course with strings being returned and received in this simple app. Time to start making something slightly more complex...a simple genetic algorithm simulator!
Part 6: “New Obstacles”
Okay so new project, Parcel - installed, Rust module - created, stdweb - installed lets get this show on the road! In my head the idea is simple, make a struct in Rust to represent the Genetic Algorithm Simulation, add some methods to it to get the population or simulate a generation, and then simply instantiate and use it in JS. Can’t be too hard surely (foreshadowing)! Lets just make the struct, seems to be instantiating in JS, lets add some methods onto the struct… wait...what? It seems exporting structs is temperamental at best when using stdweb and parcel am I back to square 1 already?
Part 7: “The Choice”
All seems lost, I’m out of viable Rust packages to try and have a console littered with errors, is there nothing I can do? In a last ditch effort I tried manually compiling the .wasm file myself and importing it but after 5 edits to the Rust file I can already feel this getting tedious… As I crawl through GitHub issue after GitHub issue webpack comes up again and again as the solution, maybe I need to accept defeat and go back.
Part 8: “Crisis”
F*** I’m going to have to use Webpack, I think as I go back to the start and open the Webpack Rust template, defeated.
Part 9: “Epiphany”
As the Webpack Rust template repo clones I took to Google one last time, using one of my old searches, hoping for a miracle. Wait, what's this? A GitHub issue about Parcel and WASM_Bindgen which wasn’t there before? The Google search index must have only just found this to be relevant? Hold on, someone has linked a template here for Rust, WASM_Bindgen, and Parcel! Thank the Search Engine Gods the project may be saved!
Part 10: “Resolution”
There it was, under my nose the whole time on the rustwasm GitHub repository. I quickly cloned the repo and followed the set-up instructions and it all worked flawlessly. In the end this search had become a real Cinderella story with the perfect match being found on the stroke of midnight.
So now, time to make something cool with it! I didn’t want to focus too much on the front end and slaving over SCSS making it look nice, so I turned to an old friend: TailWindCSS, a utility-first CSS framework which I have set up with PostCSS and Parcel before. With all that done I build out a simple layout with a side panel for configuring the simulation and a main panel to hold the results of the simulation. After deciding on the look and feel of the page I start to make some TypeScript components for controlling and displaying the simulation.
Finally after getting the site up and running with some mock data from a simple set_interval
I start to hook it up to the WASM. It ends up being remarkably simple, just import the module
object from the Rust projects’ cargo.toml
and then all the structs and functions are attached to it! A few little tweaks and testing and what do you know, it's all working and converging! A little bit of cleanup and then I deploy it on Firebase and it’s hosted happily ever after.
Conclusion
Now this article has been a bit of fun writing in this style and talking about a project I’ve genuinely enjoyed every minute of, and every up and down. But, what is it actually like using Rust and Parcel? I can wholeheartedly say it is a true pleasure...once you find the right resources. Parcel just makes it so easy with no configuration needed for most projects, and although it might not be as fast on larger projects it will give the big dogs a run for their money 9/10 times!
As for Rust and WASM it was everything I expected and more. Rust has always been a language I have loved programming in and, although it's a challenge, it never gets old. However, if I am to complain about one thing about this experience, it would be the lack of intellisense on the exported JS module. It may not be an issue when you write the tiny Rust file being compiled but I can see this being painful on larger projects using Rust, WASM, and Parcel.
In conclusion, if you have ever had a little voice telling you to give Rust or WASM a go, I would highly recommend it and maybe consider using Parcel to avoid the emotional rollercoaster I went on to get it done!
Top comments (12)
Awesome journey and congratulations to your first post :D
I started with Rust and WebAssembly too a few weeks ago.
But I went from reading the "The wasm-bindgen Guide" to reading "Programming WebAssembly with Rust" to simply using wasm_bindgen/wasm-pack to build a library.
I saw an update of Parcel on Twitter a few days ago where they mentioned static WASM imports and .rs files, so I was intrigued.
After reading your post I was happy that I didn't start with Parcel, tho' :D
What's the story behind WASM library creation with Parcel? Or should I simply stick to wasm-pack, which seems okay-ish right now.
Thanks so much and glad you enjoyed the article and have been having some fun with WASM and Rust too!
The import .rs feature on Parcel is really amazing and works well for small programs where you might want to move some performant functions to Rust but I've found doesn't scale too well!
As for the Parcel vs wasm-pack I would definitely agree wasm-pack is more established and has the official backing from Rust. The reason I've been a fan of Parcel for a while now has been the 0 configuration nature which has let me get projects up and running super quickly (simple as importing a .ts file in the HTML and then TypeScript is setup for you)! This definitely has it's drawbacks and I wouldn't use Parcel on a large project yet but for little things like this and getting something off the ground I'm hardpressed to find something that beats it :)
Hi, guys Seed maintainer here.
There is a Rust-only template for Seed apps, but it shouldn't be hard to modify it if you don't want to use Seed. The heart is cargo-make - it's a very nice cross-platform task runner and it can install things like
wasm-pack
automatically.I see you like Tailwind - perhaps production ready webpack & tailwind template would be interesting for you.
I'm just rewriting Seed official guides from scratch, but once ready I'll be working on a "one-command starter" for Rust apps - Seeder.
Cheers, Martin
Hi Martin,
I remember looking into Seed a little while ago and am thinking of exploring it further given another break from project work! Also the webpack and tailwind template would be interesting as setting up PostCSS with Parcel was another dependency I didn't want to set up.
Looking forward to seeing Seed develop and this one-command started!
Thanks for reading :)
Thanks for the post! I went through a similar experience getting my wasm/Rust app started; having a desire to cut back on the number of npm dependencies needed to run/build a primarily wasm application.
In the end, I went with a home-rolled setup using nginx (in Docker) and a Procfile to run
cargo watch
andwasm-pack
. Getting the perks of hot reloading/ease of use with a Parcel/Webpack setup is the direction I would point others in though.Interestingly, the number of dependencies that need to be installed to build a Parcel app is larger than a barebones webpack app. That led me to snowpack, but it seems to be right in the middle of a v2 transition and the docs for writing plugins are not there yet. I'm interested to see where it goes though as I'm a fan of fewer moving parts/pieces.
Glad you like the post! I think I was going down the same path as you with Docker at one point but hastily escaped that rabbit hole but sounds like you found a nice solution with it!
That is a interesting investigation I forgot to look into, I would expect that this is due to Parcel coming pre-bundled with a few plugins and dependencies out of the box than barebones WebPack, something for me to consider in the future!
I had never heard of snowpack before seeing this but sounds and looks really interesting I will have to give this a look for a project I'm about to start! :)
The code's at github.com/CoffeeAndCode/remnant-c... if you're interested, but I would not recommend it's current state. It currently lints/tests before building which is way too long to wait for new wasm builds. Also, for a better Docker solution I'd package more of the build/web server into a single Docker container for ease of installation.
I believe you're right on the install size differences. Parcel is trying to make a better dev experience which doesn't include finding an array of packages to install then digging through docs on how to use them.
Snowpack seems interesting, but make sure it fits your end goals before investing too much. It's goals are a bit different than the other packagers out there.
Thanks again!
It's funny, how for me Parcel is the ex I cannot work with - especially because it is impossible to link a file which is not managed by Parcel, which makes 90% of my projects unbuildable with Parcel. WebPack is my packager of choice, because I can easily decide, how certain files are handled, what to exclude or how to queue certain processing steps to build exactly what I want.
Having said all that, I recently stumbled across a new bundler, which seems to be a cross-over between WebPack and Parcel. It's WebPack, but automated. And they say that it's fast. Have you ever checked out SnowPack?
JFYI, I made a short article on how to set it up a while ago: metta.systems/blog/building-wasm-w...
This was all I needed to start going.
Nice concise writeup, wish I had seen this before I went on this journey! I'll have to give this approach a go next time I have some free time to work on some WASM :)
Great read. Awesome job on your first post 😃 And this was a cool pairing, thanks for sharing your insight with all of us!
Thanks so much! :) Hope this helped you and would love to hear if you make anything using WASM!