はじめに
本連載では、Webフロントエンド開発において注目されているバイナリ形式のプログラムファイルWebAssemblyを、Rustで実装して活用する方法を説明しています。
より本格的にプログラムを開発するには、正しく動作させるまでにデバッグを行う必要があります。本記事ではデバッグ時に利用できるログ出力について説明します。また、アプリの読み込み速度をより速くできる、ファイルサイズ縮小方法についても述べます。
完成したプログラムの再利用に備えて、実装したWebAssemblyをパッケージとして世界に公開しておくと便利です。本記事の後半では、WebAssemblyのプログラムを、Node.jsのパッケージマネージャーnpmで利用できるパッケージとして世界に公開する方法と、パッケージを参照して利用する方法を紹介します。
対象読者
- Rust/WebAssemblyのプログラムが思う通りに動かず試行錯誤している方
- 速度を追求するためファイルサイズを縮小したい方
- 実装成果を再利用したり、世界に公開したりしたい方
必要な環境
本記事のサンプルコードは、以下の環境で動作を確認しています。
- Windows 10 64bit版
- Microsoft Edge 111.0.1661.54
- rustup 1.25.2
- wasm-pack 0.11.0
- Node.js v18.15.0 64bit版
今回のサンプルコード(p004-use-image-processor以外)では、WebAssemblyをビルドするRustのプロジェクトの内部に、WebAssemblyを参照するWebページのプロジェクト(wwwサブフォルダー)を含めています。プロジェクトを実行するには「wasm-pack build」コマンドでWebAssemblyをビルド後、wwwサブフォルダーで「npm install」コマンドを実行してライブラリーをインストールし、「npm run start」コマンドでプログラムを実行します。実行内容はWebブラウザーで「http://localhost:8080」を表示して確認できます。プロジェクトの生成法については、第1回も参考にしてください。p004-use-image-processorサンプルは「npm install」→「npm run start」で実行できます。
なお、Node.jsに含まれるOpenSSLと、Webページプロジェクトが利用するwebpackとの非互換により「npm run start」が失敗する場合があるので、そのときは環境変数「NODE_OPTIONS」に「--openssl-legacy-provilder」を設定してください。PowerShellの場合は、リスト1のコマンドで環境変数を設定できます。
$env:NODE_OPTIONS = "--openssl-legacy-provider"
デバッガーとログ出力
Rust/WebAssemblyの公式Webページに掲載されたデバッグについての記述では、Rust/WebAssemblyのデバッガー環境がまだ成熟していないことに言及されています。Webブラウザーの開発者ツールを利用しても、Rustのコードではなく生のWebAssembly命令がステップ実行されるため、わかりづらいのが現状です。
現在現実的に利用できる方法として、ログを出力する処理をソースコードに追加して、Webブラウザーの開発者ツール(コンソール)でそのログを確認する方法があります。Rust/WebAssenbly開発でログ出力を利用する例を、図2のサンプルで説明します。console.logとconsole.errorによるログ出力をRust/WebAssemblyから実行します。
また、意図的にパニック(回復不可能なエラー)を発生させたとき、事前に「パニックフックを有効化」ボタンを押しておくと、パニックが発生した場所を出力できます。
console.log / errorでRustからログ出力する実装
JavaScriptのconsole.log / errorを出力する処理は、Rustでリスト2の通り実装します。
extern crate web_sys; // ... web_sysを利用(1) (略) // 引数が1つのconsole.log ...(2) #[wasm_bindgen] pub fn console_log(s: &str) { web_sys::console::log_1(&s.into()); } // 引数が2つのconsole.error ...(3) #[wasm_bindgen] pub fn console_error(s1: &str, s2: &str) { web_sys::console::error_2(&s1.into(), &s2.into()); }
(1)で、ログ出力処理を含むweb_sysクレートを参照します。(2)がconsole.log、(3)がconsole.errorの処理です。web_sys::console::log_1、web_sys::console::error_2メソッドを利用してログ出力します。各メソッドの「_1」、「_2」は、引数に指定する文字列の数を表します。引数の文字列で「&<変数名>.info()」としているのは、文字列をJavaScriptで利用する値(JsValue)に変換するためです。
「log_1」~「log_7」および「error_1」~「error_7」の各メソッドが提供されており、ログ出力時に最大で7個の引数を受け取れます。さらに、ログ出力する任意個の文字列を、JsValueの配列で指定できる「web_sys::console::log」「web_sys::console::error」メソッドもあります。ログ出力メソッドの詳細は公式ドキュメントも参照してください。
これらのメソッドはweb_sysクレートに含まれるので、Cargo.tomlにweb_sysクレートの指定を、リスト3の通り追加します。
[dependencies.web-sys] version = "0.3" features = [ "console" ]
パニック時のログ出力
Rustでは、実行中に回復不可能なエラーが発生したときに「パニック」が発生します。このサンプルでは、リスト4(1)のpanic!マクロで意図的にパニックを発生させます。
#[wasm_bindgen] pub fn exec_panic(s: &str) { panic!("パニック発生!詳細は->{}", s); // ...(1) }
パニック発生時はWebブラウザーのコンソールにエラーが出力されますが、その内容は図3(a)の通りわかりづらいものです。これを図3(b)の通りわかりやすくするには、リスト5(1)の通り、set_panic_hookメソッドをあらかじめ実行しておきます。
#[wasm_bindgen] pub fn enable_panic_hook() { console_log("console_error_panic_hookを有効にしました"); utils::set_panic_hook(); // ...(1) }
set_panic_hookメソッドは、プロジェクトにデフォルトで含まれるutil.rsにリスト6の通り実装されています。
pub fn set_panic_hook() { #[cfg(feature = "console_error_panic_hook")] // feature指定 ...(1) console_error_panic_hook::set_once(); // パニックフック指定 ...(2) }
(1)は、featureに「console_error_panic_hook」が指定されたときに処理を行うことを表します。(2)の処理で、わかりやすいエラーログを出力する機能を有効にします。(1)の「console_error_panic_hook」は、Cargo.tomlにリスト7の通りデフォルト設定されているため、(2)のパニックフック指定が有効になります。
[features] default = ["console_error_panic_hook"]