WebAssembly 準備

用意するもの

The Rust Toolchain

  • rustup
  • rustc
  • cargo

wasm-pack

ビルド、テスト、WebAssemblyの生成のためのワンストップショップ

https://rustwasm.github.io/wasm-pack/installer/

curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh

cargo-generate

gitリポジトリにあるテンプレートから Rustプロジェクトを生成する

インストール

cargo install cargo-generate

使い方

WebAssembly サンプルプロジェクトの生成

cargo generate --git https://github.com/rustwasm/wasm-pack-template

npm

サーバーサイド。 生成した .wasm を npmレジストリーに公開する。

npm install npm@latest -g

wasmのロード

Node.js ビルド版

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Hello wasm-pack!</title>
  </head>
  <body>
    <noscript>This page contains webassembly and javascript content, please enable javascript in your browser.</noscript>
    <script src="./bootstrap.js"></script>
  </body>
</html>

bootstrap.js

// A dependency graph that contains any wasm must all be imported
// asynchronously. This `bootstrap.js` file does the single async import, so
// that no one else needs to worry about it again.
import("./index.js")
  .catch(e => console.error("Error importing `index.js`:", e));
wasm-pack build

--target を付けなければ、 webpack対応版が作られる。

index.js

// hello-wasm-pack側でwasmがロードされている
import * as wasm from "hello-wasm-pack";

wasm.greet();

hello-wasm-pack.js

import * as wasm from "./hello-wasm-pack_bg.wasm";
export * from "./hello-wasm-pack_bg.js";

web版

wasm-pack build --target web

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Hello wasm-pack!</title>
  </head>
  <body>
    <noscript>This page contains webassembly and javascript content, please enable javascript in your browser.</noscript>
    <script type="module" src="./index.js"></script>
  </body>
</html>
<html>
  <head>
    <meta content="text/html;charset=utf-8" http-equiv="Content-Type"/>
  </head>
  <body>
    <!-- Note the usage of `type=module` here as this is an ES6 module -->
    <script type="module">
      // Use ES module import syntax to import functionality from the module
      // that we have compiled.
      //
      // Note that the `default` import is an initialization function which
      // will "boot" the module and make it ready to use. Currently browsers
      // don't support natively imported WebAssembly as an ES module, but
      // eventually the manual initialization won't be required!
      import init, { add } from './pkg/without_a_bundler.js';

      async function run() {
        // First up we need to actually load the wasm file, so we use the
        // default export to inform it where the wasm file is located on the
        // server, and then we wait on the returned promise to wait for the
        // wasm to be loaded.
        //
        // It may look like this: `await init('./pkg/without_a_bundler_bg.wasm');`,
        // but there is also a handy default inside `init` function, which uses
        // `import.meta` to locate the wasm file relatively to js file.
        //
        // Note that instead of a string you can also pass in any of the
        // following things:
        //
        // * `WebAssembly.Module`
        //
        // * `ArrayBuffer`
        //
        // * `Response`
        //
        // * `Promise` which returns any of the above, e.g. `fetch("./path/to/wasm")`
        //
        // This gives you complete control over how the module is loaded
        // and compiled.
        //
        // Also note that the promise, when resolved, yields the wasm module's
        // exports which is the same as importing the `*_bg` module in other
        // modes
        await init();

        // And afterwards we can use all the functionality defined in wasm.
        const result = add(1, 2);
        console.log(`1 + 2 = ${result}`);
        if (result !== 3)
          throw new Error("wasm addition doesn't work!");
      }

      run();
    </script>
  </body>
</html>

type=moduleでない方式

<html>
  <head>
    <meta content="text/html;charset=utf-8" http-equiv="Content-Type"/>
  </head>
  <body>
    <!-- Include the JS generated by `wasm-pack build` -->
    <script src='pkg/without_a_bundler_no_modules.js'></script>

    <script>
      // Like with the `--target web` output the exports are immediately
      // available but they won't work until we initialize the module. Unlike
      // `--target web`, however, the globals are all stored on a
      // `wasm_bindgen` global. The global itself is the initialization
      // function and then the properties of the global are all the exported
      // functions.
      //
      // Note that the name `wasm_bindgen` can be configured with the
      // `--no-modules-global` CLI flag
      const { add } = wasm_bindgen;

      async function run() {
        await wasm_bindgen('./pkg/without_a_bundler_no_modules_bg.wasm');

        const result = add(1, 2);
        console.log(`1 + 2 = ${result}`);
      }

      run();
    </script>
  </body>
</html>

index.js

import * as wasm from "./pkg/bindgen_sample.js";

wasm.default().then((module) => {
    module.greet("Tanaka")
})

hello-wasm-pack.js

// 省略
async function init(input) {
    if (typeof input === 'undefined') {
        input = import.meta.url.replace(/\.js$/, '_bg.wasm');
    }
    const imports = {};
    imports.wbg = {};
    imports.wbg.__wbg_alert_83023aa40c1985f5 = function(arg0, arg1) {
        alert(getStringFromWasm0(arg0, arg1));
    };

    if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) {
        input = fetch(input);
    }

    const { instance, module } = await load(await input, imports);

    wasm = instance.exports;
    init.__wbindgen_wasm_module = module;

    return wasm;
}

// wasmが取得できる
export default init;

参照

rustwasm.github.io