WebAssembly -Part II.A | Getting Started with Rust

Francisco Vilches
May 5, 2019 · 8 min read
Image for post
Image for post

The Introduction Before the Introduction

Last time, we started off by explaining the core concepts of WebAssembly and how you can try it for yourself with little to no setup. If you need a refresher, please refer to this link.

This article is more of a how-to on getting up and running with a production-grade web project which incorporates Rust (or any other language for that matter) and WebAssembly into your web pages.

Introduction

Nowadays, as front and back end developers we’re used to having tools save us time and avoid human errors by performing mundane tasks such as code compilation, style checking, code organization, optimization, etc.

In particular, when writing web applications we can set up a bundler or task runner to seamlessly transform, optimize, and bundle our files. Naturally, if incorporating WebAssembly in parts of our code, we expect to have our tools also do lots of heavy lifting for us.

So all being said, this is the type of picture we want to get to paint:

Image for post
Image for post
Webpack

And in case you’re wondering: YES you can update your code (Rust, C++, Go, etc.) and have live-reloading or hot-module-replacement automatically display any changes.

Why Rust?

The beauty of WebAssembly is that it’s agnostic. What do I mean by that?

Any language with a compiler compiling down to Wasm can be used for web development.

Image for post

So in theory, you could use multiple languages to develop your web application.

Therefore, the question “Why Rust?” is a bit misleading. It sounds like you’re stuck with one Wasm-supported language for you web application.

However, at the time of writing, languages like Rust and C/C++ have better support compared to other languages. By support I mean:

This is mainly due to the fact that currently WebAssembly supports just flat linear memory. That’s all good for languages like C/C++/Rust, but other modern languages need a garbage collector to run.

But it’s not to say other languages aren’t progressing though. Check out Microsoft’s project Blazor for example.

Lastly, the consensus on the web seems to be: Rust is a safer and faster language than C/C++

So in essence, we’re left with this current picture:

Image for post
Image for post

Less talk, more code. How to Setup your Project

Ok, I’ll try a bit less rambling. What we’re going to do now is build our typical hello world application.

For a more complete example you can refer to the Wasm Math Helper further down in the article.

What are we building?

It’s as simple as clicking a button and having WebAssembly (i.e. Rust code) update the DOM to display a greeting.

Image for post
Image for post

That’s right, we’re not updating the DOM with JavaScript 😎.

Source Code

Isn’t WebAssembly overkill for this?

Absolutely

So when does it make sense to use WebAssembly?

I’d use WebAssembly wherever there’s performance bottlenecks in a Web or NodeJs application. E.g. imagine your JavaScript code relies on some slow hashing function. So why not use a really fast (even existing) C/C++ library right in the browser?

Project Pre-Requisites

Step 1 [Init]

Open a terminal like command prompt or bash and type in:

mkdir hello-wasm && cd hello-wasm && npm init -y

Step 2 [Install Dev Dependencies]

npm i -D webpack webpack-cli webpack-dev-server clean-webpack-plugin html-webpack-plugin

Step 3 [Create Your Source Folder]

Open up your editor of choice in this directory.

Make sure to create your app.js and index.html files in the src directory

Image for post
Image for post

Step 3 [Creating the Web Page]

Paste the below in your index.html file.

Step 4 [Creating the Rust Lib]

In your command prompt or bash make sure you’re inside the src/ directory. Type in the following:

$ cargo new hello_world --lib

What are we doing here?

Cargo is a tool that allows Rust packages to declare their various dependencies and ensure that you’ll always get a repeatable build. -Rust-Lang documentation-

So we’re now left the following:

Image for post
Image for post

lib.rs is where we’re going to write our actual Rust code.

Cargo.toml among other things, is going to ensure our dependencies are fetched and built.

Step 5 [Declare Rust Dependencies]

Paste the below into your Cargo.toml file:

crate: in Rust world, this is synonymous to library or package.

[lib] to specify we’re creating a library, not an executable.

[dependencies] wasm-bindgen this library will facilitate high-level interactions between wasm modules and JavaScript. Plus, it’ll help us automatically generate a package containing JavaScript-helper code that’s used to call the .wasm modules we’ll be generating.

[dependencies.web-sys] well, the Rust people explain it well:

It provides raw bindings to all the Web’s APIs: everything from DOM manipulation to WebGL to Web Audio to timers to fetchand more!

Step 6 [Creating the Rust Greeting Code]

Back in the lib.rs file get rid of the boiler plate code that’s been generated for you, and paste in the following:

Notice the [wasm_bindgen] attribute at the top of the greet() function. Soon enough this attribute is going to help us package up our .wasm and JS helper files when using the wasm-pack tool (which we’ll get to shortly).

Also, notice how the web_sys crate gives us seamless access to the DOM. Yes, the HTML DOM!

You can already start imagining using powerful Rust or C/C++ libraries for things like rendering graphics in an HTML5 canvas for example…

Step 7 [Packaging up Rust with Wasm-Pack]

In part, wasm-pack is a tool that reads and packages up our Rust libraries with helper JS and .Wasm files. These are the files that we’re going to use in our web application.

Let’s see it in action. In your command prompt or bash make sure you’re in the ./src/hello_world/ directory. Type in:

wasm-pack build

You should end up with a pkg/ directory containing the files that we’ll use for our web app:

Image for post
Image for post

Notice the following files:

hello_world.js(or .ts) instead of us directly accessing hello_world_bg.wasm file, this JS file contains helper code that does that for us. Easy!

package.json this is where the money is for me. With wasm-pack you could even go a step further and develop libraries with Rust and then publish them to npm/yarn etc. You’re users won’t even know they’re using highly performant wasm.

Step 8 [Automating wasm-pack]

Image for post
Image for post

Ok, this article’s meant to show you how to make your wasm dev life easier. So that being said, scratch step 7. Yes, scratch step 7.

Delete the pkg and target directories.

Now, in your terminal make sure you cd to the root of your project and install the following plugin:

npm i -D @wasm-tool/wasm-pack-plugin

This plugin will track our Rust libraries and automatically re-create the pkg directory in step 7.

We’ll use this plugin soon enough.

Step 9 [Configure Webpack]

In the root of your project create the file webpack.config.js and paste into it the following code:

In the above I’ve added standard webpack config code. But what’s more interesting is the WasmPackPlugin.

Image for post
Image for post

Thanks to this plugin, every time we build our Web App (i.e. run Wepback) our Rust code will be automatically compiled and packaged up in the respective pkg/ directory we saw in the previous steps.

Step 10 [Configure Package.json]

In your package.json file’s script section, add the following scripts:

"scripts": {"build": "webpack --env.production --colors","start": "webpack-dev-server --env.development --colors","start-in-prod-mode": "webpack-dev-server --env.production --colors --open"}

Back in your terminal, make sure you cd into the root of the project and type in:

npm start

This should automatically rebuild rust pkg directory

Image for post
Image for post

Note: running the other two scripts will cause the Rust code to be compiled in production/release mode, thus generating more compact and efficient wasm files.

Step 11 [app.js]

Paste the following in your app.js

Once the web app users click on the greet button, the code will dynamically import the src/hello_world/pkg/hello_world.js’s greet() function. This function will then help us access the hello_world.wasm file that’s going to update the doc greeting (i.e. the <h1> element).

Step 12 [Let’s see it in action]

By this time, you’re project structure should look like this:

Image for post
Image for post

Now, in your terminal and from the root of the project, run:

npm start

Click on the greet button

Image for post
Image for post

And voilà!!!

Image for post
Image for post

Step 13 [Update the Rust code & see it straight away!]

Make sure the webpack-dev-server is still running. Otherwise, run the npm start command again.

Now go back to the lib.rs file in the src/hello_world/src/ directory. Update the greeting to anything you prefer.

Image for post
Image for post

Save your file and go back to the web page on your browser. After you press the greet button again you’ll see your new greeting.

Image for post
Image for post

Wasm Math Helper

If you want to take more of a deep dive, I recommend checking out another application I built: WASM Math Helper

Image for post
Image for post

It’s a one page application and contains math utilities.

The key point is that the same utility is replicated. One is written with good old fashioned JavaScript, and the other is written with Rust. Both implement the exact same algorithms. As such, we can compare JS and Wasm performance.

Where’s the code?

GitHub | Working Example

Further reading

WebAssembly -Part II.b | Golang with WASM

Learn Rust

Learn Rust + Wasm

MDN [Rust to Wasm]

tech-lah

Technical news & blogs

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store