DEV Community

Hasan Yousef
Hasan Yousef

Posted on

Simple Rust + WASM example

  1. Install rust
$ brew install rustup
$ rustup-init
Enter fullscreen mode Exit fullscreen mode
  1. Set default toolchain as nightly
$ rustup default nightly
Enter fullscreen mode Exit fullscreen mode
  1. Ass wasm target
$ rustup target add wasm32-unknown-unknown
Enter fullscreen mode Exit fullscreen mode
  1. Install wasm-gc tool to remove all unneeded exports, imports, functions, and so on from the generated WebAssembly module.
$ cargo instal wasm-gc
Enter fullscreen mode Exit fullscreen mode
  1. Instal https to runs a web server, serving static files from the current directory
$ cargo instal https
Enter fullscreen mode Exit fullscreen mode
  1. Create rust app, and open its files with your IDE (I'm using idea)
$ cargo new --lib utils
$ cd utils
$ idea .
Enter fullscreen mode Exit fullscreen mode

7.Define the rust CDI lib type in the cargo.toml:

[package]
name = "utils"
version = "0.1.0"
authors = ["Hasan Yousef <hasan.ajsf@gmail.com>"]
edition = "2018"

[dependencies]

[lib]
crate-type =["cdylib"]
Enter fullscreen mode Exit fullscreen mode
  1. Define the extern function:
#[no_mangle]
pub extern fn add_one(x: u32) -> u32 {
    x + 1
}
Enter fullscreen mode Exit fullscreen mode

The extern keyword is needed to create an interface, so that this function can be invoked from other languages.
The no-mangle annotation to tell the Rust compiler not to mangle the name of this function.

  1. Build the wasm file:
$ cargo build --target wasm32-unknown-unknown --release
Enter fullscreen mode Exit fullscreen mode
  1. Run the wasm-gc to optimize the wasm file:
$ wasm-gc target/wasm32-unknown-unknown/release/utils.wasm -o utils.gc.wasm
Enter fullscreen mode Exit fullscreen mode
  1. Create the index.html file, and call the wasm module through javascript:
<!DOCTYPE html>
<html>
  <head>
    <script> 
      WebAssembly.instantiateStreaming(fetch("utils.gc.wasm"))
        .then(wasmModule => {
          const result = wasmModeult.instance.exports.add_one(3);
          const text = document.createTextNode(result);
          document.body.appendChild(text);
        });
    </script>
  <head>
  <body></body>
<html>
Enter fullscreen mode Exit fullscreen mode

Using instantiateStreaming instead, we can stream, compile, and instantiate a WebAssembly module in one go.

  1. Run the static file server:
$ http
Enter fullscreen mode Exit fullscreen mode
  1. Open your browser at: localhost:8000

ADVANCE

If you want to interact with JavaScript function, you need to:

  1. Define these functions signature in the rust file
  2. Define a bridge/wrapper in the javascript file between these functions

So, if want to call the javascript alert and another function, let's say, addOne, then the above main.rs and index.html files will be as below:

main.rs:

// To call a JavaScript function
// 1. Define the JS functions signatures
extern {
    fn addOne(x: u32);
    fn alert(x: u32);
}
// 2. Call the JS function using unsafe block
#[no_mangle]
pub extern fn add_one(x: u32) {
    unsafe {
        addOne(x);
        alert(x);
    }
}
Enter fullscreen mode Exit fullscreen mode

index.html

<!DOCTYPE html>
<html>
  <head>
    <script> 
  const addOne = (number) => {
    const text = document.createTextNode(number + 1);
    document.body.appendChild(text);
  }

  const importObject = {
    env: {
        addOne: addOne,
        alert: alert,
    }
  };

  WebAssembly.instantiateStreaming(fetch("utils.gc.wasm"), importObject)
  .then(wasmModule => {
       const result = wasmModule.instance.exports.add_one(5);
   })
    </script>
  <head>
  <body></body>
<html>
Enter fullscreen mode Exit fullscreen mode

For using bindgen, kindly see my other post

Latest comments (4)

Collapse
 
robinvanemden profile image
Robin van Emden

Thank you for a great introductory post!

I found one minor typo in your index.html code:

wasmModeult.instance.exports.add_one

should of course be

wasmModule.instance.exports.add_one
Collapse
 
pojntfx profile image
Felicitas Pojtinger

WASM is awesome. I'd recommend using parcel as a bundler though, it's much simpler to get started with.

import { add } from "./add.rs"

const sum = add(1,2)

console.log(sum)
Enter fullscreen mode Exit fullscreen mode
Collapse
 
qm3ster profile image
Mihail Malo

It automatically applies bindgen?

Collapse
 
pojntfx profile image
Felicitas Pojtinger

Not yet: #647