WASM by Rust - Tutorial

Page content

Tutorial

The URL I followed.

https://developer.mozilla.org/en-US/docs/WebAssembly/Rust_to_wasm

Pre-requirement

  1. You should install npm beforehand.
  2. To compile wasm-pack, apt install -y build-essential and install gcc.
  3. In case of Ubuntu, apt install -y libssl-dev pkg-config.

Download wasm-pack

To build the package, we need an additional tool, wasm-pack. This helps compile the code to WebAssembly, as well as produce the right packaging for npm.

cargo install wasm-pack

Write codes

cargo new --lib hello-wasm
cd hello-wasm

src/lib.rs

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern {
    pub fn alert(s: &str);
}

#[wasm_bindgen]
pub fn greet(name: &str) {
    alert(&format!("Hello, {}!", name));
}

wasm-bindgen facilitate high-level interactions between Wasm modules and JavaScript.

Cargo.toml

[package]
name = "hello-wasm"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
description = "A sample project with wasm-pack"
license = "MIT/Apache-2.0"
repository = "https://github.com/yourgithubusername/hello-wasm"
edition = "2018"

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

[dependencies]
wasm-bindgen = "0.2"

Build Rust code into npm package (WASM+JavaScript)

$ wasm-pack build
[INFO]: Checking for the Wasm target...
[INFO]: Compiling to Wasm...
   Compiling proc-macro2 v1.0.36
   Compiling unicode-xid v0.2.2
   Compiling wasm-bindgen-shared v0.2.78
   Compiling log v0.4.14
   Compiling syn v1.0.84
   Compiling cfg-if v1.0.0
   Compiling lazy_static v1.4.0
   Compiling bumpalo v3.8.0
   Compiling wasm-bindgen v0.2.78
   Compiling quote v1.0.14
   Compiling wasm-bindgen-backend v0.2.78
   Compiling wasm-bindgen-macro-support v0.2.78
   Compiling wasm-bindgen-macro v0.2.78
   Compiling hello-wasm v0.1.0 (/home/atlex00/rust-projects/hello-wasm)
    Finished release [optimized] target(s) in 7.56s
:-) [WARN]: origin crate has no README
[INFO]: License key is set in Cargo.toml but no LICENSE file(s) were found; Please add the LICENSE file(s) to your project directory
[INFO]: Installing wasm-bindgen...
[INFO]: Optimizing wasm binaries with `wasm-opt`...
[INFO]: :-) Done in 9.44s
[INFO]: :-) Your wasm pkg is ready to publish at /home/atlex00/rust-projects/hello-wasm/pkg.

The new directory pkg was created:

pkg
├── hello_wasm.d.ts
├── hello_wasm.js
├── hello_wasm_bg.js
├── hello_wasm_bg.wasm
├── hello_wasm_bg.wasm.d.ts
└── package.json

This pkg directory is your hello world npm package directory. package.json was created based on Cargo.toml.

Add this generated package to npm.

cd pkg
npm link

Use the hello-wasmnpm package from Node.js

In a clean new directory (~/site):

~/site
➜ npm link hello-wasm

added 1 package, and audited 3 packages in 545ms

found 0 vulnerabilities

~/site
➜ ls
node_modules

~/site
➜ tree
.
└── node_modules
    └── hello-wasm -> ../../hello-wasm/pkg

2 directories, 0 files

Create package.json:

{
  "scripts": {
    "serve": "webpack-dev-server"
  },
  "dependencies": {
    "hello-wasm": "^0.1.0"
  },
  "devDependencies": {
    "webpack": "^4.25.1",
    "webpack-cli": "^3.1.2",
    "webpack-dev-server": "^3.1.10"
  }
}

Create webpack.config.js:

const path = require('path');
module.exports = {
  entry: "./index.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "index.js",
  },
  mode: "development"
};

Create index.html:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>hello-wasm example</title>
  </head>
  <body>
    <script src="./index.js"></script>
  </body>
</html>

Create index.js:

import("./node_modules/hello-wasm/hello_wasm.js").then((js) => {
  js.greet("WebAssembly");
});

Build and use it!

npm install
npm run serve

Check here when npm run serve returned error.

Bundle it

npm install -g webpack webpack-cli
webpack

webpack command creates dist directory, and here, you can find a full bundled JavaScript files:

➜ tree dist/
dist/
├── 0.index.js
├── 8f9bcf343e2abc668c79.module.wasm
└── index.js

Other take-aways

How the index.js called WASM?

Check the package before it bundled:

➜ cat pkg/hello_wasm.js 
import * as wasm from "./hello_wasm_bg.wasm";
export * from "./hello_wasm_bg.js";

And here is the first line of ./hello_wasm_bg.js:

import * as wasm from './hello_wasm_bg.wasm';

I guess, this link would be the minimalistic explanation:

Loading and running WebAssembly code - MDN Web Docs

For hardcore low-levelers

When you want to check the WAT (WebAssembly Text) format of the .wasm file, you can try this wasm2wat page(official repository from WebAssembly.org).

Troubleshooting

When npm run serve returns error like:

node:internal/crypto/hash:67
  this[kHandle] = new _Hash(algorithm, xofLen);
                  ^

Error: error:0308010C:digital envelope routines::unsupported

Run export NODE_OPTIONS=--openssl-legacy-provider on the terminal, and try npm run serve again.