Rust ライブラリは、動的リンクライブラリとしてビルドすることで、WebAssembly ライブラリを作成できます。 ここでは、WebAssembly ライブラリから、JavaScript 関数の呼び出し方法を説明します。
cargo でライブラリプロジェクトを作成します。
$ cargo new wasm_js --lib $ cd wasm_js
Cargo.toml に以下を追加します。
[lib] crate-type = ['cdylib']
Rustでは、WebAssembly に限らず、crate_type
に cdylib
を指定することで、他の言語から呼び出すことのできる動的リンクライブラリをビルドすることができます(通常のライブラリであれば、Linux の場合 *.so
、 macOS の場合 *.dylib
、Windowsの場合*.dll
ファイルが作成されることになります)。
lib.rs
を以下のように編集します。
#[link(wasm_import_module = "env")] extern { fn hello(); } #[no_mangle] pub extern "C" fn say() { unsafe { hello();} }
WebAssemblyのモジュールは一連のimportを宣言し、それぞれモジュール名とimport名を持っています。
#[link(wasm_import_module)]
でモジュール名を定義し、JavaScript から提供される hello()
関数を宣言しています。
JavaScript で提供する hello()
関数を、JavaScript からキックするために say()
を外部に公開します。
ここでは、WebAssembly ライブラリをブラウザから呼び出すので、WASMのビルドターゲットを以下のように指定してビルドします。
$ cargo build --target wasm32-unknown-unknown
もしビルドターゲットが未導入の場合には以下で追加することができます。
$ rustup target add wasm32-unknown-unknown
ビルドが成功すれば target/wasm32-unknown-unknown/debug/wasm_js.wasm
が作成されます。
cargo プロジェクトのルートに index.html
を以下のように作成します。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>Hello Web Assembly</title> <script> const importObj = { env: { hello: () => document.body.textContent = `hello` } } fetch('target/wasm32-unknown-unknown/debug/wasm_js.wasm') .then(response => response.arrayBuffer()) .then(buffer => WebAssembly.instantiate(buffer, importObj)) .then(({module, instance}) => instance.exports.say()) </script> </head> <body></body> </html>
importObj
で定義した関数を WebAssembly.instantiate()
の引数として与えます。
Httpサーバを起動します。例えば以下の何れかなどになるでしょう。
$ npx serve # npm $ jwebserver # java18以降 $ python3 -m http.server 8000 # python
以下の出力を得ることができます。
ブラウザが instantiateStreaming
をサポートしていれば、以下のように書くこともできます。
const importObj = { env: { hello: () => document.body.textContent = `hello2` } } WebAssembly.instantiateStreaming( fetch('target/wasm32-unknown-unknown/debug/wasm_js.wasm'), importObj) .then(({module, instance}) => instance.exports.say())
WebAssemblyと外部とのやり取りには、整数や浮動小数点数などのプリミティブ データ型しか扱うことができません。 WebAssembly はいくつかの数値型のみをサポートし、これがエクスポートされた関数を介して返すことができる全てです。
例えば文字列をやり取りする場合は、共有されたメモリ内の場所への参照と、文字列の長さをやり取りする必要があります。 そのため、通常は wasm-bindgen などを使い、ブリッジを自動生成させて使うことになります。