Subterranean Flower

Rust単体でWebAssemblyをコンパイルする(Emscripten無し)

Author
古都こと
ふよんとえんよぷよぐやま!!!

Rustのターゲットにwasm32-unknown-unknownが追加され、Rust単体でのWebAssemblyのコンパイルが可能になりました。

この記事では、RustのみでWebAssemblyをコンパイルする手順を説明します。

EmscriptenなしでのWebAssemblyのコンパイル

WebAssemblyを使ってRustのコードをブラウザ上で実行するでは、Emscriptenを使用したWebAssemblyの利用方法を紹介しました。

しかしEmscriptenという外部ツールを利用することと、大量のグルーコードが必要になり、純粋にWebAssemblyを利用するという面では、あまり効率的とは言えませんでした。

しかし今回Rustのターゲットにwasm32-unknown-unknownが追加されたことで、Emscripten無しでのWebAssemblyの利用が可能になりました。

Rustのインストール

WebAssemblyの開発にあたり、まずはRustの開発環境を整える必要があります。

Rust本体のインストール

まずはRust本体をインストールします。すでに済んでいる方はこの手順は飛ばしてください。

まずはrustupをインストールします。rustupはRust開発環境のインストールを補助するツールです。

$ curl https://sh.rustup.rs -sSf | sh

次にターミナルを再起動するか、sourceコマンドでPATHを再読み込みしてください。

$ source ~/.bash_profile

これでrustupが使用可能になるので、Rustのコンパイラ等をインストールします。

現在、wasm32-unknown-unknownをサポートしているのはnightly版になるので、nightlyをインストールします。

なお、nightly版のみになるのは、この記事が公開された段階でのことなので、将来的にstable版でのサポートが行われている可能性があります。十分に調べた上でインストールチャンネルを選択してください。

$ rustup install nightly

WebAssemblyターゲットの追加

Rustがインストールできたら、次にターゲットを追加します。

$ rustup target add wasm32-unknown-unknown --toolchain nightly

これで開発準備が整いました!

Rust側の関数をJavaScriptから呼び出す

次のようなRustの関数があるとします。これをsum.rsとします。

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

関数sumはその名の通りふたつの引数の合計を返す関数です。これをJavaScriptから呼び出してみましょう。

このとき関数に#[no_mangle]をつけることを忘れないでください。#[no_mangle]はマングリングを防ぐためのものです。マングリングというのは、簡単に言うと、コンパイル時にコンパイラの都合のいいように名前を勝手に変更する機能です。#[no_mangle]を指定することで、関数「sum」には余計なことをせず、確実に「sum」として出力されるようになります。

これをコンパイルします。

$ rustc +nightly --target wasm32-unknown-unknown -O sum.rs --crate-type=cdylib

これでsum.wasmが出力されたと思います。これをJavaScript側から利用します。Fetch APIを使うと簡単に読み込むことができます。

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <script>
      fetch('sum.wasm')
        .then((response) => response.arrayBuffer())
        .then((bytes) => WebAssembly.instantiate(bytes, {}))
        .then((results) => {
          const instance = results.instance;
          console.log(instance.exports.sum(2, 3));
        });
    </script>
  </head>
  <body>
  </body>
</html>

WebAssembly.instantiateはWebAssemblyをインスタンス化するためのAPIで、WebAssembly.Instanceオブジェクトが得られます。これによりJavaScript側からWebAssemblyが利用可能になります。

WebAssembly.Instanceオブジェクトはexportsプロパティを持ち、その中にRust側の関数が入っています。今回は「sum」関数なので、exports.sumですね。sum関数を使って2と3を足して表示してみましょう。これで5が表示されるはずです。

これをローカルで開くなり、サーバを立てるなりして実行します。

Rustの関数がちゃんと実行されました!