💼

RustのWasm周りのクレートまとめと、Trunkを使ってRustで書いたWasmをブラウザ上で簡単に実行する

2022/06/11に公開1

はじめに

この記事ではRustのWasmを使う際に便利なライブラリたちをまとめて、その後Trunkというツールを利用してRustでWasmを書いて実行するところまでを試してみます。

記事執筆時のRustのバージョンは1.61.0です。

RustでのWasmの実行周りのエコシステムまとめ

RustはWasmをコンパイルのターゲットとして公式でサポートしています。
wasm32-unknown-unknownというターゲットをrustupで追加することでWasm向けにRustをコンパイルできます。

ただしこれ単体で使うより、さらに周辺ライブラリを組み合わせて使うことが多いようです。
Rustでは周辺のライブラリなどを使うことでより便利にWasmを利用できます。
以下にRustでWasmを使う場合にお世話になるであろう周辺ライブラリなどを紹介します。

また、今回紹介しきれない範囲などについて、wasm-bindgenのガイドにとても詳しくRustでWasmを触るための何もかもが書かれているのでそちらも適宜読むと良いでしょう。

wasm-bindgen

ドキュメント

wasm-bindgenはWasmとJavaScriptの間の糊付けを担当します。
例えば次のように#[wasm_bindgen]をつけて関数を宣言することで、Rustで書いたWasmの中からJavaScriptの関数を利用したり、逆にRustで書いたWasmの関数をJavaScriptから呼べるようになります。

main.rs
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern "C" {
    fn alert(s: &str);
}

#[wasm_bindgen]
pub fn greet(name: &str) {
    alert(&format!("Hello, {}!", name));
}
index.js
import { greet } from "./hello_world";

greet("World!");

現在、Wasmは実行する際にはJavaScriptの内部から呼び出す必要があり、JavaScriptとの糊付けのコードはだいたい必須となるため、wasm-bindgenは多くの場合必須となるでしょう。

wasm-bindgenはJsValueというJavaScriptのオブジェクトをRustで表現した型を持っています。
このJsValueをブラウザAPIの型として扱う場合、例えばNumber型であったりArray型のようなJavaScript固有の型、あるいはHtmlInputElementなどのようなブラウザのオブジェクト固有の型については、後述のjs_sys及びweb_sysクレートを使います。

wasm-bindgenのserde-serializeというfeatureを有効にすることで、JsValueとJSONへシリアライズ/デシリアライズできる値との変換ができるようになります。
これによってJavaScriptから渡されるObjectをserdeでRustに取り込むことなどが可能になります。
場合によってはこの機能は後述のserde-wasm-bindgenを利用したほうが効率が良いかもしれません。

wasm-bindgen-cli

ドキュメント

wasm-bindgen-cli0は、前述のwasm-bindgenをつけてビルドしたRustの生成するWasmを食わせて新たに使いやすいWasmやJavaScriptコードなどを生成するツールです。
wasm-bindgen-cliを利用すると、Wasmファイルの他に糊付けのためのJavaScriptコードも吐き出してくれるので、JavaScriptからまるでWasmの関数を直接呼び出したりするように使うことができます。

糊付けのコードの種類に応じて次のtargetを--target [target name]として指定できます。

  • bundler: Webpackなどでバンドルするためのライブラリとしての糊付けコード
  • web: ブラウザから読み込まれるモジュールとして使う糊付けコード
  • nodejs: Node.jsのrequireで読み込める形式としての糊付けコード
  • deno: Denoからインポートできるモジュール用の糊付けコード
  • no-modules: ブラウザから読み込まれるモジュールだが、Webと違いES Moduleに対応していない環境に向けた糊付けのコード

targetについては詳しくはドキュメントのページを読むと良いでしょう。

例えば上記のmain.rs--target webで呼び出す場合、次のようなwasm_bindgen_test.jswasm_bindgen_test_bg.wasmというバイナリが生成されています。
(ついでにwasm_bindgen_test_bg.wasm.d.tswasm_bindgen_test.d.tsというTypeScriptの型定義ファイルも生成されますが、これはオプションで無効化もできます)

wasm_bindgen_test.js
wasm_bindgen_test.js

let wasm;

const cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });

cachedTextDecoder.decode();

let cachegetUint8Memory0 = null;
function getUint8Memory0() {
    if (cachegetUint8Memory0 === null || cachegetUint8Memory0.buffer !== wasm.memory.buffer) {
        cachegetUint8Memory0 = new Uint8Array(wasm.memory.buffer);
    }
    return cachegetUint8Memory0;
}

function getStringFromWasm0(ptr, len) {
    return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));
}

let WASM_VECTOR_LEN = 0;

const cachedTextEncoder = new TextEncoder('utf-8');

const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
    ? function (arg, view) {
    return cachedTextEncoder.encodeInto(arg, view);
}
    : function (arg, view) {
    const buf = cachedTextEncoder.encode(arg);
    view.set(buf);
    return {
        read: arg.length,
        written: buf.length
    };
});

function passStringToWasm0(arg, malloc, realloc) {

    if (realloc === undefined) {
        const buf = cachedTextEncoder.encode(arg);
        const ptr = malloc(buf.length);
        getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf);
        WASM_VECTOR_LEN = buf.length;
        return ptr;
    }

    let len = arg.length;
    let ptr = malloc(len);

    const mem = getUint8Memory0();

    let offset = 0;

    for (; offset < len; offset++) {
        const code = arg.charCodeAt(offset);
        if (code > 0x7F) break;
        mem[ptr + offset] = code;
    }

    if (offset !== len) {
        if (offset !== 0) {
            arg = arg.slice(offset);
        }
        ptr = realloc(ptr, len, len = offset + arg.length * 3);
        const view = getUint8Memory0().subarray(ptr + offset, ptr + len);
        const ret = encodeString(arg, view);

        offset += ret.written;
    }

    WASM_VECTOR_LEN = offset;
    return ptr;
}
/**
* @param {string} name
*/
export function greet(name) {
    const ptr0 = passStringToWasm0(name, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
    const len0 = WASM_VECTOR_LEN;
    wasm.greet(ptr0, len0);
}

async function load(module, imports) {
    if (typeof Response === 'function' && module instanceof Response) {
        if (typeof WebAssembly.instantiateStreaming === 'function') {
            try {
                return await WebAssembly.instantiateStreaming(module, imports);

            } catch (e) {
                if (module.headers.get('Content-Type') != 'application/wasm') {
                    console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e);

                } else {
                    throw e;
                }
            }
        }

        const bytes = await module.arrayBuffer();
        return await WebAssembly.instantiate(bytes, imports);

    } else {
        const instance = await WebAssembly.instantiate(module, imports);

        if (instance instanceof WebAssembly.Instance) {
            return { instance, module };

        } else {
            return instance;
        }
    }
}

async function init(input) {
    if (typeof input === 'undefined') {
        input = new URL('wasm_bindgen_test_bg.wasm', import.meta.url);
    }
    const imports = {};
    imports.wbg = {};
    imports.wbg.__wbg_alert_0bebe9e6d7aece39 = 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;
}

export default init;

#[wasm_bindgen]をつけて宣言したgreet関数が用意されているのがわかります。
WasmとJavaScriptのデータのやり取りにはUint8Arrayなどの特定の型を使う必要があるため、文字列をエンコード及びデコードする仕組みも合わせて書き出されています。

また、Wasmの処理時にWasmのサイズの最適化も行ってくれます。
Rustがデフォルトで吐き出すWasmにはリリースビルドでもデバッグ用情報などが含まれており、かなりサイズが大きいものとなります。
例えば先のmain.rscargo build --releaseした場合、私の手元の環境では1655KBのWasmが吐き出されました。
これをwasm-bindgen-cliに食べさせた結果吐き出されるWasmは26KBでした。
大きくファイルサイズが減っていることがわかります。

Trunkを使う場合、wasm-bindgenの適用は勝手にやってくれるので、直接wasm-bindgen-cliを叩く必要はないかもしれません。

js_sys

ドキュメント

js_sysはJavaScriptの型をRustで扱うためのクレートです。
JavaScriptのNumber型やJsString型などを提供します。

web_sys

ドキュメント

web_sysはブラウザのAPIやブラウザ固有の型をRustで表現したものとなります。
HtmlInputElementやHtmlDivElementなどのブラウザ固有の型を扱う際に必要になります。

特にEventTargetを特定の型として取得する場合など、ダイナミックなキャストが必要になる場面も多く、その場合dyn_castなどを使います。
dyn_castResult型を返すので、dyn_castunwrapが入り乱れるなかなか治安の悪いコードを書くことになります。

js_sysやweb_sysでまるでWasmの中から直接JavaScriptの値やDOMを触っているような書き味でプログラムをかけますが、裏側ではJavaScriptのグルーコードを大量に生成してwasmにバインドしたそれらの関数の呼び出しなどに振り替えているようなので、WasmとJavaScriptの世界のやり取りが必要なためそこまで効率は良くないかもしれません。
「WebAssembly Reference Types」を利用すると生成されるJavaScriptのグルーコードが減り、効率も多少良くなることが期待されます。
「WebAssembly Reference Types」については次の記事がよくまとまっていて参考になります。

2022年6月11日の現状ではTrunkを使うとこのreference typesはうまく動きません。
Trunkにはdata-reference-typesという属性が指定できるようになっていますが、これによる指定ではwasm-bindgenのオプションのみ切り替わり、wasm-optのオプションが渡されないためリリースビルドでエラーが出てしまいます。
この問題についてはこちらのPRで直りそうに見えますが、現状まだ取り込まれていないようです。

wasm-bindgen-futures

ドキュメント

Wasm環境下ではAtomicなオブジェクトを標準で使うのはまだ整備されていないため、ArcやMutexなどの仕様が制限されます。
それによってtokioなどの非同期ランタイムがWasm上ではうまく動きません。

wasm-bindgen-futuresはWasm環境下でも動く非同期ランタイムを提供します。
この非同期ランタイムはJavaScriptのPromiseの実行と結びついており、JavaScriptのPromiseとRustのFutureを相互に変換するような仕組みも用意してくれます。

gloo

GitHub

WasmからDOMを操作したり、ブラウザのAPIを利用したりする際には、決まりきったコードを何度も書く必要が出てきます。
そのような処理を切り出して、ブラウザAPIをRustから叩く際の辛さを軽減してくれるライブラリです。

serde-wasm-bindgen

GitHub

前述したserde-serializeというfeatureはJsValueとRustの値のやり取りにJSONのシリアライズとデシリアライズの機構を使っていました。
これは数値型などの直接扱えるものでもJSONを経由しているということで若干のムダがあります。
こちらのserde-wasm-bindgenはJSONを経由せずに直接やり取りできるものはやり取りするため、コードサイズが小さくなったりMapやSetをやり取りできるあたりが優れているとのことです。

wasm-pack

公式ページ

wasm-packはwasm-bindgenをラップしたより扱いやすいCLIツールです。

次のようにしてインストールが可能です。

$ cargo install wasm-pack

wasm-packを使うことで、ビルドしてwasm-bindgen-cliでさらに変換処理を行い、npmで配布可能なパッケージにするという多段階必要なビルド手順をまとめることが可能です。
またnewコマンドのようなテンプレートから生成する機能などもあり、より手軽にWasmの実行を行えます。

後述のTrunkとの違いは、wasm-packはwebpackなどのJavaScript側で使われるバンドラーなどと合わせて使うことが想定されていることです。
webpackと合わせて使わない場合には今回のテーマであり後述するTrunkを利用したほうがより簡単にWasmの実行を行えます。

wasm-opt

GitHub

binaryenというリポジトリでwasm-optというツール含むいくつかのツールが作られています。
wasm-optはその名の通りWasmの最適化をしてくれるツールです。
与えるオプションによって実行速度の最適化ではなくファイルサイズの最適化ができます。

後述のTrunkに組み込まれていて、直接使うことなく最適化を行えます。

前述のmain.rsをwasm-bindgenに通したあとの26KBのWasmを、さらにwasm-optに最適化オプション-Ozのファイルサイズを小さくするような設定でかけてみると、17KBとなりました。

twiggy

GitHub

twiggyはWasmバイナリファイルの内訳を表示することなどができます。
ファイルサイズの確認などに使えます。

上記main.rsをリリースビルドしたWasmをtwiggy topにかけてみた結果は次のとおりです。

twiggy top main.wasm
$ twiggy top .\target\wasm32-unknown-unknown\release\wasm_bindgen_test.wasm
 Shallow Bytes │ Shallow % │ Item
───────────────┼───────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
        59935535.39% ┊ custom section '.debug_str'
        38462322.71% ┊ custom section '.debug_info'
        29570017.46% ┊ custom section '.debug_line'
        21456812.67% ┊ custom section '.debug_pubnames'
        1622809.58% ┊ custom section '.debug_ranges'
          87770.52% ┊ "function names" subsection
          49170.29% ┊ dlmalloc::dlmalloc::Dlmalloc<A>::malloc::hcec496a2198179fe
          39320.23% ┊ custom section '.debug_abbrev'
          14290.08% ┊ dlmalloc::dlmalloc::Dlmalloc<A>::free::h5fba2897807212aa
          11190.07% ┊ __rdl_realloc
          10620.06% ┊ core::str::count::do_count_chars::h8068e62995a060d4
          10310.06% ┊ core::fmt::Formatter::pad_integral::ha4f8dd1eb5755fda
           9870.06% ┊ core::fmt::Formatter::pad::hc84bfca380790d9b
           7700.05% ┊ data[0]
           7270.04% ┊ dlmalloc::dlmalloc::Dlmalloc<A>::dispose_chunk::h15f89ab1d45de9ae
           7040.04% ┊ core::fmt::write::h3e26ed3eda73f0a3
           5580.03% ┊ alloc::fmt::format::h588637f2f2183a4d
           5010.03% ┊ dlmalloc::Dlmalloc<A>::malloc::hecadd78677587853
           4940.03% ┊ __externref_table_alloc
           3930.02% ┊ dlmalloc::dlmalloc::Dlmalloc<A>::release_unused_segments::h4826643b506d455c
           3780.02% ┊ <alloc::string::String as core::fmt::Write>::write_char::hfcad3e975f1a678c
           3760.02% ┊ alloc::string::String::push::he882a8f290dcb8c3
           3640.02% ┊ core::fmt::num::imp::fmt_u64::h0c2fe44c27dae252
           3520.02% ┊ dlmalloc::dlmalloc::Dlmalloc<A>::insert_large_chunk::h0ab04c16e556807e
           3460.02% ┊ dlmalloc::dlmalloc::Dlmalloc<A>::unlink_large_chunk::h3cd39bebabb3864d
           3240.02% ┊ compiler_builtins::mem::memcpy::h78a567d3a58ab6e5
           3240.02% ┊ custom section '.debug_pubtypes'
           3210.02% ┊ __externref_heap_live_count
           3170.02% ┊ __externref_table_dealloc
           3080.02% ┊ <std::panicking::begin_panic_handler::PanicPayload as core::panic::BoxMeUp>::take_box::h5fff44acdfbac4e7
           3020.02% ┊ std::panicking::rust_panic_with_hook::he2a025723e105e28
           2630.02% ┊ custom section '__wasm_bindgen_unstable'
           2220.01% ┊ alloc::raw_vec::RawVec<T,A>::reserve::do_reserve_and_handle::h2d7a97b6f3432e07
           2220.01% ┊ alloc::raw_vec::RawVec<T,A>::reserve_for_push::h20a6726722873456
           2220.01% ┊ alloc::raw_vec::RawVec<T,A>::reserve::do_reserve_and_handle::h1397f88c4b936258
           2220.01% ┊ alloc::raw_vec::RawVec<T,A>::reserve_for_push::h00dea0a6d40548b6
           2030.01% ┊ <std::panicking::begin_panic_handler::PanicPayload as core::panic::BoxMeUp>::get::h79854d279d39b63d
           1940.01% ┊ std::panicking::begin_panic_handler::{{closure}}::hd9f8c213ec91b9d5
           1890.01% ┊ greet
           1840.01% ┊ alloc::raw_vec::finish_grow::h98b255e8c87ba6ac
           1840.01% ┊ alloc::raw_vec::finish_grow::h7642e1a95a96c333
           1570.01% ┊ dlmalloc::dlmalloc::Dlmalloc<A>::init_top::h18355a6f078d958f
           1500.01% ┊ core::result::unwrap_failed::h68fdaca771c68bb6
           1340.01% ┊ std::alloc::default_alloc_error_hook::h0876b7eb4919815d
           1180.01% ┊ <&mut W as core::fmt::Write>::write_fmt::he72a95b1e7bc46cb
           1180.01% ┊ <&mut W as core::fmt::Write>::write_fmt::hcb1df0cddb10ef35
            940.01% ┊ core::fmt::Formatter::pad_integral::write_prefix::h9ddec898718cb90a
            910.01% ┊ rust_begin_unwind
            900.01% ┊ <&mut W as core::fmt::Write>::write_str::h3169351e95b802c7
            900.01% ┊ <&mut W as core::fmt::Write>::write_str::hd4b6ba77fb1193cc
            860.01% ┊ core::panicking::panic::hc28e7cc2aa792341
            800.00% ┊ <std::panicking::begin_panic_handler::StrPanicPayload as core::panic::BoxMeUp>::take_box::h8e753108593700f0
            800.00% ┊ alloc::raw_vec::capacity_overflow::h1de937e4490b6dee
            780.00% ┊ core::panicking::panic_fmt::hb02133958c1e7d35
            670.00% ┊ import __wbindgen_externref_xform__::__wbindgen_externref_table_set_null
            670.00% ┊ custom section 'producers'
            630.00% ┊ import __wbindgen_externref_xform__::__wbindgen_externref_table_grow
            600.00% ┊ __wbindgen_describe___wbg_alert_0bebe9e6d7aece39
            590.00% ┊ <dlmalloc::sys::System as dlmalloc::Allocator>::alloc::h8b3509d622dedd6b
            560.00% ┊ import __wbindgen_placeholder__::__wbg_alert_0bebe9e6d7aece39
            550.00% ┊ __wbindgen_malloc
            510.00% ┊ export "__wbindgen_describe___wbg_alert_0bebe9e6d7aece39"
            510.00% ┊ rust_panic
            490.00% ┊ __externref_drop_slice
            470.00% ┊ import __wbindgen_placeholder__::__wbindgen_describe
            440.00% ┊ __wbindgen_realloc
            440.00% ┊ core::ptr::drop_in_place<std::error::<impl core::convert::From<alloc::string::String> for alloc::boxed::Box<dyn std::error::Error+core::marker::Sync+core::marker::Send>>::from::StringError>::h9ce0522b145fe6fa
            440.00% ┊ core::ptr::drop_in_place<std::panicking::begin_panic_handler::PanicPayload>::h636ad6d68e279e0d
            410.00% ┊ dlmalloc::dlmalloc::Chunk::set_inuse::hd80515990c160bc8
            400.00% ┊ dlmalloc::dlmalloc::Segment::holds::h1a60be87238e9919
            390.00% ┊ rust_oom
            370.00% ┊ dlmalloc::dlmalloc::Chunk::set_free_with_pinuse::h64fe80d61b111714
            360.00% ┊ custom section 'name' headers
            350.00% ┊ <core::fmt::Error as core::fmt::Debug>::fmt::hb67b0fb623137665
            340.00% ┊ core::option::Option<T>::unwrap::haa480509e27ff8fc
            320.00% ┊ dlmalloc::dlmalloc::Chunk::set_inuse_and_pinuse::hb9f654c37e5fb327
            310.00% ┊ elem[0]
            300.00% ┊ export "__externref_heap_live_count"
            300.00% ┊ core::option::Option<T>::unwrap::h3bbfb90b442ebef6
            300.00% ┊ dlmalloc::dlmalloc::TreeChunk::leftmost_child::h8bd9454cc3d402c7
            280.00% ┊ export "__wbindgen_describe_greet"
            280.00% ┊ export "__externref_table_dealloc"
            270.00% ┊ custom section '__wasm_bindgen_unstable' headers
            260.00% ┊ export "__externref_table_alloc"
            260.00% ┊ __wbindgen_exn_store
            260.00% ┊ std::sys_common::backtrace::__rust_end_short_backtrace::h6efd730283875809
            260.00% ┊ <&T as core::fmt::Debug>::fmt::hea96acd558b28457
            250.00% ┊ export "__externref_drop_slice"
            250.00% ┊ __rust_realloc
            240.00% ┊ __wbindgen_free
            240.00% ┊ dlmalloc::dlmalloc::Chunk::set_size_and_pinuse_of_free_chunk::h11a8a5342df751be
            230.00% ┊ export "__wbindgen_exn_store"
            220.00% ┊ <&T as core::fmt::Display>::fmt::hcaadeff9a1654879
            220.00% ┊ <std::panicking::begin_panic_handler::StrPanicPayload as core::panic::BoxMeUp>::get::h486eccde219d443e
            220.00% ┊ <&T as core::fmt::Display>::fmt::h8228500dd0a93de5
            210.00% ┊ export "__wbindgen_realloc"
            210.00% ┊ __rust_alloc
            200.00% ┊ export "__wbindgen_malloc"
            200.00% ┊ <&mut W as core::fmt::Write>::write_char::h735ba2e2255ac07c
            200.00% ┊ dlmalloc::dlmalloc::leftshift_for_tree_index::h6fffd47072ccebc0
            200.00% ┊ custom section '.debug_pubnames' headers
            190.00% ┊ <&mut W as core::fmt::Write>::write_char::h7f2928ac137cbae7
            190.00% ┊ core::fmt::num::imp::<impl core::fmt::Display for u32>::fmt::h6bf974e8791b3fa6
            190.00% ┊ custom section '.debug_pubtypes' headers
            180.00% ┊ export "__wbindgen_free"
            180.00% ┊ dlmalloc::dlmalloc::align_up::hf1f4ea6608931a25
            180.00% ┊ custom section '.debug_ranges' headers
            170.00% ┊ __rust_dealloc
            170.00% ┊ dlmalloc::dlmalloc::left_bits::h4c3ee1c04a9d948c
            170.00% ┊ dlmalloc::dlmalloc::Chunk::clear_pinuse::h28b4b6bfa0365301
            170.00% ┊ custom section '.debug_abbrev' headers
            160.00% ┊ <str as core::fmt::Display>::fmt::he02814d332326528
            160.00% ┊ memcpy
            160.00% ┊ custom section '.debug_info' headers
            160.00% ┊ custom section '.debug_line' headers
            150.00% ┊ __rust_alloc_error_handler
            150.00% ┊ <T as core::any::Any>::type_id::hf1eb4553966c7029
            150.00% ┊ <T as core::any::Any>::type_id::hf86124a39f31439b
            150.00% ┊ dlmalloc::dlmalloc::Chunk::cinuse::hfcc18d9ee069b807
            150.00% ┊ dlmalloc::dlmalloc::Chunk::inuse::h86972c896758fa35
            150.00% ┊ dlmalloc::dlmalloc::Segment::top::had8e50e872afff9d
            150.00% ┊ core::intrinsics::const_eval_select::hbff2fd2057209df4
            150.00% ┊ core::ops::function::FnOnce::call_once::h7538d6968a96c4c6
            150.00% ┊ alloc::alloc::handle_alloc_error::rt_error::h215dddd0f4495f99
            150.00% ┊ alloc::alloc::handle_alloc_error::h0693183370a7902c
            150.00% ┊ __rg_oom
            150.00% ┊ core::ops::function::FnOnce::call_once::h5753b14cff916f0e
            150.00% ┊ <T as core::any::Any>::type_id::hc91c1766c0a8e26b
            150.00% ┊ custom section '.debug_str' headers
            140.00% ┊ export "__heap_base"
            140.00% ┊ __rdl_alloc
            140.00% ┊ dlmalloc::dlmalloc::Chunk::set_size_and_pinuse_of_inuse_chunk::h81fc9155883489ea
            130.00% ┊ export "__data_end"
            130.00% ┊ dlmalloc::dlmalloc::Chunk::mmapped::h04354a447c7ef83b
            120.00% ┊ <() as wasm_bindgen::describe::WasmDescribe>::describe::h8066b3287422c15b
            120.00% ┊ <str as wasm_bindgen::describe::WasmDescribe>::describe::h09e5dc149b174418
            120.00% ┊ __rdl_dealloc
            120.00% ┊ dlmalloc::dlmalloc::least_bit::h5164fc99df6558f5
            120.00% ┊ dlmalloc::dlmalloc::Chunk::size::ha342d078107e0f53
            120.00% ┊ dlmalloc::dlmalloc::Chunk::pinuse::h3e366cef7e528bba
            120.00% ┊ dlmalloc::dlmalloc::Segment::is_extern::h6fdd496f2978bb35
            120.00% ┊ dlmalloc::dlmalloc::Segment::sys_flags::h4d6b01219f9e07b9
            120.00% ┊ custom section 'producers' headers
            110.00% ┊ wasm_bindgen::externref::internal_error::h1e4496ca9551d147
            110.00% ┊ wasm_bindgen::__rt::malloc_failure::h8137027072480bce
            110.00% ┊ code section headers
            100.00% ┊ type[13]: (i32, i32, i32, i32, i32, i32) -> i32
            100.00% ┊ wasm_bindgen::__rt::link_mem_intrinsics::h84678cad5a3f00f2
             90.00% ┊ type[12]: (i32, i32, i32, i32, i32) -> i32
             90.00% ┊ export "memory"
             90.00% ┊ dlmalloc::dlmalloc::Chunk::plus_offset::hf594a71617f2958c
             90.00% ┊ dlmalloc::dlmalloc::Chunk::minus_offset::ha660e14327ac3e4c
             90.00% ┊ dlmalloc::dlmalloc::Chunk::to_mem::h4b386d4e48d965cf
             90.00% ┊ dlmalloc::dlmalloc::Chunk::from_mem::h75b6b5095597031a
             90.00% ┊ dlmalloc::dlmalloc::TreeChunk::next::h6a3f651c010e6975
             90.00% ┊ dlmalloc::dlmalloc::TreeChunk::prev::h052e4d2e0cbc3ff0
             90.00% ┊ core::panic::panic_info::PanicInfo::message::h102fddb6becb8eab
             90.00% ┊ core::panic::panic_info::PanicInfo::location::h5dfcade149d8a598
             90.00% ┊ core::panic::panic_info::PanicInfo::can_unwind::hf0853bc09675a7a1
             80.00% ┊ type[7]: (i32, i32, i32, i32) -> i32
             80.00% ┊ type[11]: (i32, i32, i32, i32, i32) -> nil
             80.00% ┊ global[0]
             80.00% ┊ global[1]
             80.00% ┊ global[2]
             80.00% ┊ export "greet"
             80.00% ┊ <dlmalloc::sys::System as dlmalloc::Allocator>::page_size::h88a1bbbf4b8674f2
             80.00% ┊ wasm magic bytes
             70.00% ┊ type[1]: (i32, i32, i32) -> i32
             70.00% ┊ type[10]: (i32, i32, i32, i32) -> nil
             70.00% ┊ type[14]: (i64, i32, i32) -> i32
             60.00% ┊ type[2]: (i32, i32) -> i32
             60.00% ┊ type[6]: (i32, i32, i32) -> nil
             60.00% ┊ std::process::abort::hbba08b3d6df850f8
             60.00% ┊ __rust_start_panic
             60.00% ┊ dlmalloc::dlmalloc::Chunk::fencepost_head::h343fc8d31cab4021
             60.00% ┊ dlmalloc::dlmalloc::Chunk::mem_offset::hb26f0a8f126c0579
             60.00% ┊ dlmalloc::dlmalloc::TreeChunk::chunk::h9cbb25ca6296593a
             60.00% ┊ <dlmalloc::sys::System as dlmalloc::Allocator>::remap::h0979387267a4a643
             60.00% ┊ <dlmalloc::sys::System as dlmalloc::Allocator>::free_part::hef5dee6eb24bcc8c
             60.00% ┊ <dlmalloc::sys::System as dlmalloc::Allocator>::free::h0aa881b9e76b9b02
             60.00% ┊ <dlmalloc::sys::System as dlmalloc::Allocator>::can_release_part::hc2a2319ea9025de0
             50.00% ┊ type[0]: (i32, i32) -> nil
             50.00% ┊ type[4]: (i32) -> i32
             50.00% ┊ type[9]: (i32) -> i64
             40.00% ┊ type[3]: (i32) -> nil
             40.00% ┊ type[8]: () -> i32
             40.00% ┊ import section headers
             40.00% ┊ table[0]
             40.00% ┊ export section headers
             40.00% ┊ wasm_bindgen::externref::link_intrinsics::h8e3c0da2384b97d7
             40.00% ┊ core::ptr::drop_in_place<&mut std::io::Write::write_fmt::Adapter<alloc::vec::Vec<u8>>>::ha5213606bfbb1ca2
             40.00% ┊ core::ptr::drop_in_place<&u8>::h52ad2d112c7e2ce3
             40.00% ┊ core::ptr::drop_in_place<&core::iter::adapters::copied::Copied<core::slice::iter::Iter<u8>>>::h0e2bcd53588f6395
             40.00% ┊ data section headers
             30.00% ┊ type[5]: () -> nil
             30.00% ┊ type section headers
             30.00% ┊ table section headers
             30.00% ┊ memory section headers
             30.00% ┊ global section headers
             30.00% ┊ element section headers
             20.00% ┊ memory[0]
             20.00% ┊ "function names" subsection
             20.00% ┊ "function names" subsection
       1693789100.00% ┊ Σ [203 Total Rows]

リリースビルドしたにも関わらずデバッグ情報がWasmに入っています。

wasm-bindgen-cliを通したあとのwasmにtwiggyを使ってみると次の通り。

twiggy top wasn_bindgen_test_bg.wasm
$ twiggy top .\.pkg\wasm_bindgen_test_bg.wasm
 Shallow Bytes │ Shallow % │ Item
───────────────┼───────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
          692126.25% ┊ "function names" subsection
          401515.23% ┊ dlmalloc::dlmalloc::Dlmalloc<A>::malloc::hcec496a2198179fe
          11854.49% ┊ dlmalloc::dlmalloc::Dlmalloc<A>::free::h5fba2897807212aa
          10624.03% ┊ core::str::count::do_count_chars::h8068e62995a060d4
           9793.71% ┊ core::fmt::Formatter::pad_integral::ha4f8dd1eb5755fda
           9593.64% ┊ core::fmt::Formatter::pad::hc84bfca380790d9b
           8813.34% ┊ __rdl_realloc
           7702.92% ┊ data[0]
           6642.52% ┊ core::fmt::write::h3e26ed3eda73f0a3
           6232.36% ┊ dlmalloc::dlmalloc::Dlmalloc<A>::dispose_chunk::h15f89ab1d45de9ae
           5221.98% ┊ alloc::fmt::format::h588637f2f2183a4d
           3851.46% ┊ dlmalloc::Dlmalloc<A>::malloc::hecadd78677587853
           3541.34% ┊ <alloc::string::String as core::fmt::Write>::write_char::hfcad3e975f1a678c
           3521.34% ┊ alloc::string::String::push::he882a8f290dcb8c3
           3411.29% ┊ core::fmt::num::imp::fmt_u64::h0c2fe44c27dae252
           3241.23% ┊ compiler_builtins::mem::memcpy::h78a567d3a58ab6e5
           3221.22% ┊ dlmalloc::dlmalloc::Dlmalloc<A>::release_unused_segments::h4826643b506d455c
           3211.22% ┊ dlmalloc::dlmalloc::Dlmalloc<A>::unlink_large_chunk::h3cd39bebabb3864d
           3191.21% ┊ dlmalloc::dlmalloc::Dlmalloc<A>::insert_large_chunk::h0ab04c16e556807e
           2821.07% ┊ <std::panicking::begin_panic_handler::PanicPayload as core::panic::BoxMeUp>::take_box::h5fff44acdfbac4e7
           2570.97% ┊ std::panicking::rust_panic_with_hook::he2a025723e105e28
           1980.75% ┊ alloc::raw_vec::RawVec<T,A>::reserve::do_reserve_and_handle::h2d7a97b6f3432e07
           1980.75% ┊ alloc::raw_vec::RawVec<T,A>::reserve_for_push::h20a6726722873456
           1980.75% ┊ alloc::raw_vec::RawVec<T,A>::reserve::do_reserve_and_handle::h1397f88c4b936258
           1980.75% ┊ alloc::raw_vec::RawVec<T,A>::reserve_for_push::h00dea0a6d40548b6
           1850.70% ┊ <std::panicking::begin_panic_handler::PanicPayload as core::panic::BoxMeUp>::get::h79854d279d39b63d
           1760.67% ┊ alloc::raw_vec::finish_grow::h98b255e8c87ba6ac
           1760.67% ┊ alloc::raw_vec::finish_grow::h7642e1a95a96c333
           1590.60% ┊ std::panicking::begin_panic_handler::{{closure}}::hd9f8c213ec91b9d5
           1560.59% ┊ greet
           1280.49% ┊ core::result::unwrap_failed::h68fdaca771c68bb6
           1180.45% ┊ dlmalloc::dlmalloc::Dlmalloc<A>::init_top::h18355a6f078d958f
           1090.41% ┊ std::alloc::default_alloc_error_hook::h0876b7eb4919815d
           1010.38% ┊ <&mut W as core::fmt::Write>::write_fmt::he72a95b1e7bc46cb
           1010.38% ┊ <&mut W as core::fmt::Write>::write_fmt::hcb1df0cddb10ef35
           1010.38% ┊ custom section 'producers'
            860.33% ┊ core::fmt::Formatter::pad_integral::write_prefix::h9ddec898718cb90a
            820.31% ┊ <&mut W as core::fmt::Write>::write_str::h3169351e95b802c7
            820.31% ┊ <&mut W as core::fmt::Write>::write_str::hd4b6ba77fb1193cc
            730.28% ┊ core::panicking::panic::hc28e7cc2aa792341
            710.27% ┊ <std::panicking::begin_panic_handler::StrPanicPayload as core::panic::BoxMeUp>::take_box::h8e753108593700f0
            650.25% ┊ alloc::raw_vec::capacity_overflow::h1de937e4490b6dee
            640.24% ┊ core::panicking::panic_fmt::hb02133958c1e7d35
            620.24% ┊ rust_begin_unwind
            590.22% ┊ <dlmalloc::sys::System as dlmalloc::Allocator>::alloc::h8b3509d622dedd6b
            470.18% ┊ __wbindgen_malloc
            410.16% ┊ dlmalloc::dlmalloc::Chunk::set_inuse::hd80515990c160bc8
            400.15% ┊ core::ptr::drop_in_place<std::error::<impl core::convert::From<alloc::string::String> for alloc::boxed::Box<dyn std::error::Error+core::marker::Sync+core::marker::Send>>::from::StringError>::h9ce0522b145fe6fa
            400.15% ┊ core::ptr::drop_in_place<std::panicking::begin_panic_handler::PanicPayload>::h636ad6d68e279e0d
            400.15% ┊ dlmalloc::dlmalloc::Segment::holds::h1a60be87238e9919
            380.14% ┊ rust_panic
            370.14% ┊ dlmalloc::dlmalloc::Chunk::set_free_with_pinuse::h64fe80d61b111714
            360.14% ┊ __wbindgen_realloc
            350.13% ┊ import wbg::__wbg_alert_0bebe9e6d7aece39
            320.12% ┊ dlmalloc::dlmalloc::Chunk::set_inuse_and_pinuse::hb9f654c37e5fb327
            300.11% ┊ elem[0]
            300.11% ┊ dlmalloc::dlmalloc::TreeChunk::leftmost_child::h8bd9454cc3d402c7
            300.11% ┊ <core::fmt::Error as core::fmt::Debug>::fmt::hb67b0fb623137665
            280.11% ┊ rust_oom
            280.11% ┊ core::option::Option<T>::unwrap::haa480509e27ff8fc
            250.09% ┊ core::option::Option<T>::unwrap::h3bbfb90b442ebef6
            240.09% ┊ dlmalloc::dlmalloc::Chunk::set_size_and_pinuse_of_free_chunk::h11a8a5342df751be
            220.08% ┊ std::sys_common::backtrace::__rust_end_short_backtrace::h6efd730283875809
            220.08% ┊ <&T as core::fmt::Debug>::fmt::hea96acd558b28457
            210.08% ┊ export "__wbindgen_realloc"
            210.08% ┊ __rust_realloc
            210.08% ┊ <std::panicking::begin_panic_handler::StrPanicPayload as core::panic::BoxMeUp>::get::h486eccde219d443e
            200.08% ┊ export "__wbindgen_malloc"
            200.08% ┊ dlmalloc::dlmalloc::leftshift_for_tree_index::h6fffd47072ccebc0
            180.07% ┊ dlmalloc::dlmalloc::align_up::hf1f4ea6608931a25
            180.07% ┊ <&T as core::fmt::Display>::fmt::hcaadeff9a1654879
            180.07% ┊ <&T as core::fmt::Display>::fmt::h8228500dd0a93de5
            170.06% ┊ dlmalloc::dlmalloc::left_bits::h4c3ee1c04a9d948c
            170.06% ┊ __rust_alloc
            170.06% ┊ dlmalloc::dlmalloc::Chunk::clear_pinuse::h28b4b6bfa0365301
            160.06% ┊ <&mut W as core::fmt::Write>::write_char::h735ba2e2255ac07c
            160.06% ┊ custom section 'producers' headers
            150.06% ┊ dlmalloc::dlmalloc::Chunk::cinuse::hfcc18d9ee069b807
            150.06% ┊ dlmalloc::dlmalloc::Chunk::inuse::h86972c896758fa35
            150.06% ┊ dlmalloc::dlmalloc::Segment::top::had8e50e872afff9d
            150.06% ┊ <&mut W as core::fmt::Write>::write_char::h7f2928ac137cbae7
            150.06% ┊ core::ops::function::FnOnce::call_once::h5753b14cff916f0e
            150.06% ┊ core::fmt::num::imp::<impl core::fmt::Display for u32>::fmt::h6bf974e8791b3fa6
            150.06% ┊ <T as core::any::Any>::type_id::hf1eb4553966c7029
            150.06% ┊ <T as core::any::Any>::type_id::hf86124a39f31439b
            150.06% ┊ <T as core::any::Any>::type_id::hc91c1766c0a8e26b
            140.05% ┊ dlmalloc::dlmalloc::Chunk::set_size_and_pinuse_of_inuse_chunk::h81fc9155883489ea
            140.05% ┊ code section headers
            130.05% ┊ __rust_dealloc
            130.05% ┊ dlmalloc::dlmalloc::Chunk::mmapped::h04354a447c7ef83b
            120.05% ┊ dlmalloc::dlmalloc::least_bit::h5164fc99df6558f5
            120.05% ┊ dlmalloc::dlmalloc::Chunk::size::ha342d078107e0f53
            120.05% ┊ dlmalloc::dlmalloc::Chunk::pinuse::h3e366cef7e528bba
            120.05% ┊ dlmalloc::dlmalloc::Segment::is_extern::h6fdd496f2978bb35
            120.05% ┊ dlmalloc::dlmalloc::Segment::sys_flags::h4d6b01219f9e07b9
            120.05% ┊ <str as core::fmt::Display>::fmt::he02814d332326528
            120.05% ┊ memcpy
            110.04% ┊ __rust_alloc_error_handler
            110.04% ┊ core::intrinsics::const_eval_select::hbff2fd2057209df4
            110.04% ┊ core::ops::function::FnOnce::call_once::h7538d6968a96c4c6
            110.04% ┊ alloc::alloc::handle_alloc_error::rt_error::h215dddd0f4495f99
            110.04% ┊ alloc::alloc::handle_alloc_error::h0693183370a7902c
            110.04% ┊ __rg_oom
            110.04% ┊ custom section 'name' headers
            100.04% ┊ type[13]: (i32, i32, i32, i32, i32, i32) -> i32
            100.04% ┊ __rdl_alloc
             90.03% ┊ type[12]: (i32, i32, i32, i32, i32) -> i32
             90.03% ┊ export "memory"
             90.03% ┊ dlmalloc::dlmalloc::Chunk::plus_offset::hf594a71617f2958c
             90.03% ┊ dlmalloc::dlmalloc::Chunk::minus_offset::ha660e14327ac3e4c
             90.03% ┊ dlmalloc::dlmalloc::Chunk::to_mem::h4b386d4e48d965cf
             90.03% ┊ dlmalloc::dlmalloc::Chunk::from_mem::h75b6b5095597031a
             90.03% ┊ dlmalloc::dlmalloc::TreeChunk::next::h6a3f651c010e6975
             90.03% ┊ dlmalloc::dlmalloc::TreeChunk::prev::h052e4d2e0cbc3ff0
             90.03% ┊ core::panic::panic_info::PanicInfo::message::h102fddb6becb8eab
             90.03% ┊ core::panic::panic_info::PanicInfo::location::h5dfcade149d8a598
             90.03% ┊ core::panic::panic_info::PanicInfo::can_unwind::hf0853bc09675a7a1
             80.03% ┊ type[10]: (i32, i32, i32, i32) -> i32
             80.03% ┊ type[11]: (i32, i32, i32, i32, i32) -> nil
             80.03% ┊ global[0]
             80.03% ┊ export "greet"
             80.03% ┊ __rdl_dealloc
             80.03% ┊ <dlmalloc::sys::System as dlmalloc::Allocator>::page_size::h88a1bbbf4b8674f2
             80.03% ┊ wasm magic bytes
             70.03% ┊ type[8]: (i32, i32, i32) -> i32
             70.03% ┊ type[9]: (i32, i32, i32, i32) -> nil
             70.03% ┊ type[14]: (i64, i32, i32) -> i32
             70.03% ┊ type section headers
             70.03% ┊ import section headers
             70.03% ┊ table section headers
             70.03% ┊ memory section headers
             70.03% ┊ global section headers
             70.03% ┊ export section headers
             70.03% ┊ element section headers
             70.03% ┊ wasm_bindgen::__rt::malloc_failure::h8137027072480bce
             70.03% ┊ data section headers
             60.02% ┊ type[6]: (i32, i32) -> i32
             60.02% ┊ type[7]: (i32, i32, i32) -> nil
             60.02% ┊ dlmalloc::dlmalloc::Chunk::fencepost_head::h343fc8d31cab4021
             60.02% ┊ dlmalloc::dlmalloc::Chunk::mem_offset::hb26f0a8f126c0579
             60.02% ┊ dlmalloc::dlmalloc::TreeChunk::chunk::h9cbb25ca6296593a
             60.02% ┊ <dlmalloc::sys::System as dlmalloc::Allocator>::remap::h0979387267a4a643
             60.02% ┊ <dlmalloc::sys::System as dlmalloc::Allocator>::free_part::hef5dee6eb24bcc8c
             60.02% ┊ <dlmalloc::sys::System as dlmalloc::Allocator>::free::h0aa881b9e76b9b02
             60.02% ┊ <dlmalloc::sys::System as dlmalloc::Allocator>::can_release_part::hc2a2319ea9025de0
             50.02% ┊ type[3]: (i32) -> i32
             50.02% ┊ type[4]: (i32) -> i64
             50.02% ┊ type[5]: (i32, i32) -> nil
             50.02% ┊ std::process::abort::hbba08b3d6df850f8
             50.02% ┊ __rust_start_panic
             40.02% ┊ type[1]: () -> i32
             40.02% ┊ type[2]: (i32) -> nil
             40.02% ┊ table[0]
             40.02% ┊ core::ptr::drop_in_place<&mut std::io::Write::write_fmt::Adapter<alloc::vec::Vec<u8>>>::ha5213606bfbb1ca2
             40.02% ┊ core::ptr::drop_in_place<&u8>::h52ad2d112c7e2ce3
             40.02% ┊ core::ptr::drop_in_place<&core::iter::adapters::copied::Copied<core::slice::iter::Iter<u8>>>::h0e2bcd53588f6395
             30.01% ┊ type[0]: () -> nil
             20.01% ┊ memory[0]
         26366100.00% ┊ Σ [158 Total Rows]

デバッグ情報が消えました。
上位に残っているのは関数名のセクションです。

さらにwasm-optで最適化をかけたあとでは次のようになります。

twiggy top opt-wasm.wasm
$ twiggy top .\.pkg\opt-wasm.wasm
 Shallow Bytes │ Shallow % │ Item
───────────────┼───────────┼──────────────────────────────────────────
          420724.98% ┊ code[0]
          190411.30% ┊ code[2]
          10286.10% ┊ code[51]
           8855.25% ┊ code[1]
           7704.57% ┊ data[0]
           7314.34% ┊ code[15]
           7234.29% ┊ code[37]
           6413.81% ┊ code[3]
           5923.51% ┊ code[4]
           3662.17% ┊ code[5]
           3512.08% ┊ code[49]
           3492.07% ┊ code[42]
           3151.87% ┊ code[57]
           3131.86% ┊ code[7]
           3051.81% ┊ code[24]
           2951.75% ┊ code[6]
           2741.63% ┊ code[10]
           2681.59% ┊ code[9]
           1871.11% ┊ code[11]
           1871.11% ┊ code[12]
           1751.04% ┊ code[13]
           1540.91% ┊ code[14]
           1130.67% ┊ code[8]
           1050.62% ┊ code[17]
           1010.60% ┊ custom section 'producers'
            910.54% ┊ code[16]
            910.54% ┊ code[18]
            800.47% ┊ code[19]
            770.46% ┊ code[20]
            760.45% ┊ code[21]
            700.42% ┊ code[22]
            650.39% ┊ code[23]
            440.26% ┊ code[25]
            410.24% ┊ code[26]
            370.22% ┊ code[30]
            350.21% ┊ import wbg::__wbg_alert_0bebe9e6d7aece39
            340.20% ┊ code[28]
            320.19% ┊ code[27]
            320.19% ┊ code[29]
            320.19% ┊ code[31]
            300.18% ┊ elem[0]
            300.18% ┊ code[35]
            270.16% ┊ code[32]
            270.16% ┊ code[56]
            240.14% ┊ code[34]
            220.13% ┊ code[39]
            210.12% ┊ export "__wbindgen_realloc"
            210.12% ┊ code[43]
            200.12% ┊ export "__wbindgen_malloc"
            200.12% ┊ code[33]
            180.11% ┊ code[36]
            180.11% ┊ code[40]
            170.10% ┊ code[38]
            160.09% ┊ code[50]
            150.09% ┊ code[44]
            150.09% ┊ code[48]
            150.09% ┊ code[62]
            150.09% ┊ code[63]
            150.09% ┊ code[64]
            140.08% ┊ code[47]
            130.08% ┊ code[46]
            120.07% ┊ code[45]
            120.07% ┊ code[52]
            120.07% ┊ code[53]
            120.07% ┊ code[54]
            120.07% ┊ code[55]
            120.07% ┊ custom section 'producers' headers
            100.06% ┊ code[41]
             90.05% ┊ export "memory"
             90.05% ┊ code[58]
             90.05% ┊ code[59]
             90.05% ┊ code[60]
             90.05% ┊ code[61]
             80.05% ┊ type[8]: (i32, i32, i32, i32, i32) -> nil
             80.05% ┊ type[10]: (i32, i32, i32, i32) -> i32
             80.05% ┊ global[0]
             80.05% ┊ export "greet"
             80.05% ┊ wasm magic bytes
             70.04% ┊ type[2]: (i32, i32, i32) -> i32
             70.04% ┊ code section headers
             60.04% ┊ type[0]: (i32, i32) -> i32
             60.04% ┊ type[5]: (i32, i32, i32) -> nil
             50.03% ┊ type[1]: (i32, i32) -> nil
             50.03% ┊ type[3]: (i32) -> i32
             50.03% ┊ type[6]: (i32) -> i64
             50.03% ┊ code[65]
             40.02% ┊ type[4]: (i32) -> nil
             40.02% ┊ type[7]: () -> i32
             40.02% ┊ table[0]
             40.02% ┊ data section headers
             30.02% ┊ type[9]: () -> nil
             30.02% ┊ type section headers
             30.02% ┊ import section headers
             30.02% ┊ table section headers
             30.02% ┊ memory section headers
             30.02% ┊ global section headers
             30.02% ┊ export section headers
             30.02% ┊ element section headers
             20.01% ┊ memory[0]
         16844100.00% ┊ Σ [99 Total Rows]

関数名のセクションも消えて、だいぶファイルサイズが最適化されていそうなことがわかります。

Trunk

Trunkはビルドツールの一種です。
RustとWasmを組み合わせて使うように作られていますが、Wasmの他にSassからCSSへのコンバートなどWebアプリを作るのに必要なビルド機能もいくつか対応しています。
現時点ではRustで書いたWasmをnpmパッケージ等にラップすることなく直接HTML上で実行したい場合、Trunkが最速で実行できると思います。

エントリーポイントとなるindex.htmlにRustのWasmをビルドして最適化処理をかけたものを読み込む処理を埋め込み、その他リソースの処理なども行った上で、開発用サーバーを立ててくれてブラウザの自動リロードにも対応してくれます。

次項で実際にTrunkを使っていきます。

Trunkを使ってみる

まずはRustのインストールを済ませておいてください。

次にcargp installでTrunkをインストールします。

$ cargo install --locked trunk

次にこのような空っぽのhtmlファイルをindex.htmlという名前でCargo.tomlの隣に配置します。

index.html
<!DOCTYPE html>
<html>
  <head></head>
</html>

このファイルがエントリーポイントとなります。

次にいくつかのパッケージを追加します。
cargo-editが入っている場合は次のコマンドを打ちます。

$ cargo add log wasm-logger

logはRustの一般的に使われているロギングのライブラリで、wasm-loggerはconsole.logにlogクレートのログの内容を流してくれるものです。

次にmain.rsに次のように書きます。

main.rs
pub fn main() {
    wasm_logger::init(wasm_logger::Config::default());
    log::debug!("Hello, world!");
}

Trunkがwasm-bindgenとのつなぎ込みもやってくれるようなので、main関数を書くだけで良いです。
main関数はWasmをロードしたあとに勝手に走るようになっているようです。

ここまで書いたら次のコマンドを実行します。

$ trunk serve

これでビルドが走りサーバーが立ち上がります。
http://127.0.0.1:8080を開いて開発者ツールをみると次のようにHello, Worldが表示されていました。

screenshot

wasm-optの最適化オプションを付けるにはindex.htmlに次のような指定をします。

index.html
<!DOCTYPE html>
<html>
  <head>
    <link data-trunk rel="rust" data-wasm-opt="z">
  </head>
</html>

trunk serve --releaseあるいはtrunk build --releaseとすることでリリースモードで実行すると、wasm-optが実行され、Wasmのサイズが小さくなって配信に向いた形になります。

おわりに

RustでWasmを書く際に使える周辺ライブラリをまとめ、その後Trunkで実際にWasmを作成し動かすところまでやりました。
今回の記事では試しませんでしたが、JavaScriptとWasmの間でのやり取りもwasm-bindgenによってだいぶ書きやすくなっています。

次回の記事ではYewというWasm用ライブラリをTrunkを使ってビルドしてみたいと思います。

Discussion

ShinKanoShinKano

とても参考になりました!ありがとうございます✨