State Management With WebAssembly & Rust
State management in any application is always a super fun problem to solve. When it comes to integrating WebAssembly with existing applications or starting from scratch with a new project, this problem gets even more interesting, but it’s not as complicated as it may seem.
For this example, we’re going to build a super basic counter application — you’ll be able to increment and decrement the count with “+” and “-” buttons. This will cover very surface level information and basic implementation, but won’t go deep into state management patterns like “flux with Rust,” or how to build your signup form; those are posts for another time, and I do plan on covering those topics in the next little while if folks find this walk-through helpful.
Using the diagram above, we can think about our application as being three distinct parts:
- The View — our HTML document that a user would interact with
The View layer is relatively simple — a couple of buttons and a
The state layer is the most interesting and complex of the three, but if implemented properly, can actually provide a very clean interface through which we interact with our application state.
rust installed via
rustup — instructions here. You’ll also need to make sure that
wasm-pack is installed — link here.
We’ll generate the project — if you have difficulty with this step, you may need to use
npm init rust-webpack counter-app
Then we’re going to build and run the project — again, may need to use
npm run build && npm run start
You should see a blank page at
Hello world! logged in the console. If you take a look in the
src/lib.rs file, the default project is using the
wasm_bindgen Crates (Rust libraries) to generate this message.
So now that we’ve got our project up and running, we need to actually write some code. If you’re not yet familiar with Rust, I highly recommend reading through The Book.
We’re going to use some Object Oriented Programming paradigms to start out. OOP in Rust, is an interesting animal, and isn’t necessarily the most idiomatic approach, but may be an easier transition for folks coming from OOP backgrounds. We’ll cover a more functional style in a separate post.
In our Object Oriented approach, we are going to use only Rust for state management, and won’t be using
First let’s create a new file called
counter_state.rs in our
There’s a bit going on here —
First we’re creating a public Rust
struct, then we are implementing that
struct using the
wasm_bindgen must use the
The key indicator here that we’re using OOP-style Rust, is that in our
struct implementation, we are adding a public
new() method which will return an instance of the previously defined
In addition to the
new() method, we have also exposed three other public methods:
counter property on the
struct is private and isn’t exposed to the consumer.
Important: we will also need to add this
counter_state module to our imports in the
src/lib.rs file. Add the line:
mod counter_state; to the top of your file below the other imports.
The next step will be to update our
static/index.html file to include the
<button /> elements, as well as the element where we’ll display the counter state:
package.json file to provide access to our WebAssembly module by adding
"wasm": "file:pkg" to our
dependencies — you will also need to run
npm i again.
We will also need to update our
js/index.js file to import the
counter.js file, instead of the
counter.js file, we’re importing the
wasm_bindgen has generated as a binding for our Rust
struct. The generated code looks like this:
Because we now have access to this
class we also have access to the public methods on the Rust
struct— what we’re doing on line 3 of the
counter.js file is creating an instance of the
.new() method we created in our
From here, we’re setting the initial text content of the
#counter HTML element using the
get_counter() method. We’re also adding event listeners to the
<button /> elements in our HTML document, that will increment and decrement our counter’s state.
decrement_counter() methods both return the post-modification state of the private
counter property, so we don’t need to use
get_counter() a second time.
To validate that we’ve successfully implemented the counter we run:
npm i && npm run build && npm run start
localhost:8080 and you should see something that looks like this:
Object Oriented state management with Rust and WebAssembly is not only very possible, it’s actually relatively straightforward to reason about. Similar to other state management solutions, you still are creating a persistent store of some kind, and making it available as a module to your renderer — but with WebAssembly modules, you can get the performance boost for computation intensive methods, added type safety, and the other features that make Rust great.
This example only covers surface level problem spaces. We’re not using complex types or having to manage serialization or de-serialization — that does make things a little more complicated, but I will be writing another post that addresses all that in the context of
<form /> creation, in the coming weeks.
If you’re feeling like, “just show me the code!” you can view it here.