Rust & WebAssembly minimal example

01 Dec 2017 0 Comments

Files tree
├── src
│   └── main.rs
├── build
│   └── main.wasm
├── index.html
└── script.js

Preparatory work
rustup update
rustup target add wasm32-unknown-unknown --toolchain nightly
cargo install --git https://github.com/alexcrichton/wasm-gc

Files creation

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>WebAssembly</title>
    <script src="script.js"></script>
</head>
<body>
    <input type="number" id="a" value="0">+
    <input type="number" id="b" value="0">=
    <output id="c">0</output>
</body>
</html>

//The JavaScript code loads the WebAssembly module and has access to the exported function.
window.onload = function() {
    let a = document.querySelector('#a'),
        b = document.querySelector('#b'),
        c = document.querySelector('#c');

    let add;

    let calc = function() {
        c.value = add(a.value, b.value);
    };

    a.oninput = calc;
    b.oninput = calc;

    fetch('build/main.wasm')
    .then(response => response.arrayBuffer())
    .then(bytes => WebAssembly.instantiate(bytes, {}))
    .then(results => {
        add = results.instance.exports.add;
    });
};

fn main() {}

#[no_mangle]
pub fn add(a: i32, b: i32) -> i32 {
    a + b
}

Wasm generation
Generate wasm module in build folder from rs file:
rustc +nightly --target wasm32-unknown-unknown -O --crate-type=cdylib src\main.rs -o build\main.big.wasm
Reduce the size by removing unused stuff from the module:
wasm-gc build\main.big.wasm build\main.wasm
You can download wasm module here. Save as main.wasm in build folder.

Result

a + b = c


WebAssembly text format
(module
(type $t0 (func (param i32 i32) (result i32)))
(func $add (export "add") (type $t0) (param $p0 i32) (param $p1 i32) (result i32)
  (i32.add
    (get_local $p1)
    (get_local $p0)))
(table $T0 0 anyfunc)
(memory $memory (export "memory") 17)
(data (i32.const 4) "\10\00\10\00"))

Other

Http server

For local testing http server with CORS needed. A good "ready-to-use tool" option could be browser-sync:
npm install -g browser-sync
To use it:
browser-sync start --server --port 3001 --files="./*"
You don't need http server if you are using Firefox.

Batch helper script

Batch script for generation .wasm in build folder for every .rs in src folder
@echo off

for /r %%a in (src\*.rs) do call :process "%%a"
goto :eof

:process
if "%~x1"==".rs" (
    rustc +nightly --target wasm32-unknown-unknown -O --crate-type=cdylib src\%~n1.rs -o build\%~n1.big.wasm
    wasm-gc build\%~n1.big.wasm build\%~n1.wasm
    del build\%~n1.big.wasm
)