WebAssembly で Rust のコードを実行する

WebAssembly で Rust コードを JavaScript から呼び出してみました。JavaScript のMarkdown文字列をRust の Markdown パーサに渡して HTML を得て、画面上に表示するというサンプルです。

準備

※ 下記の Cloudflare Worker のコードから派生して実装したので、Cloudflare Worker 用のコードが少し混じってます。

Cloudflare Workers を Rust で使う
Cloudflare Workers を Rust で使う方法です。最小限のコードで、セットアップからデプロイまで行ってみました。

wrangler, wasm-pack をインストール

cargo install wrangler wasm-pack

wrangler generate

wrangler generate wasm-markdown-parser https://github.com/cloudflare/rustwasm-worker-template/

これは Cloudflare Workers 用のテンプレートですが、WebAssembly に必要なものが全て入ってます。(同時に、Cloudflare Worker でしか使わないコードも少し入ってしまいますが)

必要最小限のコードで進める場合は、公式の Rust から WebAssembly にコンパイルする(developer.mozilla.org) に沿って進めると良さそうです。

実装

Rust側

markdown ライブラリの追加

crates/markdown(GitHub) を使ってみます。

Cargo.toml に追加します。

[dependencies]
markdown = "0.3"
cargo build 

lib.rs を編集

src/lib.rs で JavaScript から呼ぶコードを実装します。

extern crate cfg_if;
extern crate markdown; // 追加
extern crate wasm_bindgen;

...

#[wasm_bindgen]
extern "C" {
    // JavaScript の console.log が Rust から呼べるように(デバッグ用)
    #[wasm_bindgen(js_namespace = console)]
    fn log(s: &str);
}

#[wasm_bindgen]
pub fn to_html(md_string: &str) -> String {
    let html: String = markdown::to_html(&md_string);
    // debug
    log(&format!(
        "markdown is\n {}\n\nhtml is\n{}",
        &md_string, &html
    ));
    html
}

ビルド

wasm-pack build --scope ecpplus

--scope ecpplus とすると、 npm パッケージ化されるときにパッケージ名が @ecpplus/~ となります。作成時に名称を wasm-markdown-parser としてるので、フルのパッケージ名は @ecpplus/wasm-markdown-parser となります。

npm パッケージは pkg の下に生成されます。

フロントエンド側 (JavaScript)

Rust 実装コードの呼び出し方

const js = import("./node_modules/@ecpplus/rustwasm-markdown-parser/rustwasm_markdown_parser.js");

js.then(js => {
  const htmlString = js.to_html('# This is H1')
  console.log(htmlString)
})

というようにして、WebAssembly 側で実装した to_html を呼び出すことが出来ます。

HTML 上で使ってみる

フォームの入力値を to_html に渡して画面上に表示するというのを実装してみました → https://ecpplus.net/wasm-markdown-parser/

注意点として、WebAssembly の .wasm ファイルは、 content-type を application/wasm にしないとブラウザ上で実行する事ができません。 Nginx なら mime.types に下記のように定義を追加すればOKです。

application/wasm wasm;

実装したコードを GitHub で公開しています。

ecpplus/wasm-markdown-parser(GitHub)

参考