loading...
Cover image for Simple Rust WebAssembly Boilerplate [RUST+WASM]

Simple Rust WebAssembly Boilerplate [RUST+WASM]

ajinkyax profile image Ajinkya Borade Updated on ・3 min read

You can notice the above image has mention of crates, rust macros (unused_imports) and that is all compiled via Webpack.

You can skip my introduction, if you are just interested in RUST + WebAssembly boilerplate code here

Intro

I recently got hooked to RUST programming language.
Rust initially looks very difficult, and yes it is.
But it was also a bit welcoming to me since Rust does not have
classes. (WIN WIN for me I don't like CLASS) The same reason why I liked GO very much and built a 2D RPG Game in GoLang

But since the day I started reading RUST, I stopped using GO. (In no way GO is bad, its just Rust has something magical)

Rust definitely made me understand what is a Garbage Collector (that's what I like to think so o_0), cause Rust gives its memory safety guarantees without needing a garbage collector. I really love Rust how to helped me understand Stack & Heap (I had NO computer science background, I'm just a Frontend developer... JavaScript world).

WebAssembly has become quite mature in the past couple of years. It’s certainly no longer a bleeding-edge technology. Build tools became quite sophisticated, so you can build everything with one npm command.

Folder structure :
├── src
| └── lib.rs
├── public
| └── index.js
| └── index.html
├── package.json
├── webpack.config.js
└── cargo.toml
└── web.config

rust-lang

Let's begin * _

Cargo.toml needs to define crate type as dynamic C library hence we define it like below

[lib]
crate-type = ["cdylib"]

And some special dependencies.
Please update dependencies.web-sys as you add more feature from JavaScript world.

[dependencies]
console_error_panic_hook = "=0.1.6"
js-sys = "0.3.39"
lazy_static = "1.4.0"
nalgebra = "0.21.0"
wasm-bindgen = "0.2.62"

[dependencies.web-sys]
version = "0.3.39"
features = [
    'Document',
    'Element',
    'EventTarget',
    'MouseEvent',
    'Window',
]

Next it is web.config file

This file includes Rust .wasm file into our application

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
   <system.webServer>
      <staticContent>
         <mimeMap fileExtension=".wasm" mimeType="application/wasm" />
      </staticContent>
   </system.webServer>
</configuration>

webpack.config

This is where all magic happens. Webpack compiles our Rust code into JavaScript during development and into Web-Assembly if you run npm run build

const webpack = require('webpack');
const WasmPackPlugin = require('@wasm-tool/wasm-pack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');

module.exports = (env, args) => {
  const isProduction = (args.mode === 'production');//package.json scripts -> build

  return {
      entry: './public/index.js',
      output: {
          path: path.resolve(__dirname, 'dist'),
          filename: isProduction ? '[name].[contenthash].js' : '[name].[hash].js'
      },
      plugins: [
            new HtmlWebpackPlugin({
                template: './public/index.html'
            }),
            new WasmPackPlugin({
                crateDirectory: path.resolve(__dirname, ".")// (where the cargo.toml file is located)
            }),
            new webpack.ProvidePlugin({
                TextDecoder: ['text-encoding', 'TextDecoder'],
                TextEncoder: ['text-encoding', 'TextEncoder'],
            }),
      ],
  }
};

And of course make srue you have a ./public folder with a index.html and index.JS file.

index.js

This is the main front-end file, it reads our Rust code. Please make sure you don't use ES6 imports here, use commonjs alike import to avoid compile errors. This line ../pkg/index.js webpack will create pkg folder, don't worry. Here say_hello_from_rust is actually coming from src/lib.rs file.

const rust = import('../pkg/index.js');

rust.then(r => {
    r.say_hello_from_rust();
})
.catch(console.error);

src/lib.rs

And now inside RUST ./src/lib.rs file :

wasm_bindgen created by Rust team, a library that facilitate high-level interactions between wasm modules and JavaScript.

extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern "C" {
    #[wasm_bindgen(js_namespace = console)]
    fn log(s: &str);
}

#[wasm_bindgen]
pub fn say_hello_from_rust() {
    log("Hello from Rust World!");
}


package.json

npm i @wasm-tool/wasm-pack-plugin html-webpack-plugin text-encoding webpack webpack-cli webpack-dev-server --save-dev

There are few more scripts which helps us compile the code inside package.json

But you need to add this line to your project before every build
rustup run nightly cargo build --target wasm32-unknown-unknown

So I created these helper script calls, which can be found inside package.json

npm run build which will give you production ready code to be deployed without worrying to add the above scary line every-time.

Result

After a couple of days experimenting with Rust+Wasm, it seems to be a good combo. Great performance and feature support opens a land of opportunities. Build tools are also great and Wasm should now be supported by all major browsers.

Source code: https://github.com/steelx/rust-wasm-boilerplate

Hope you have fun working with RUST and WASM. I'm definitely going to stick with RUST :)

Discussion

pic
Editor guide