今回は,wasm_bindgenでJavascriptからWasmを呼ぶときのエラーハンドリングをしてみます.具体的にはthiserrorで作成したエラーをJavascript側でキャッチします.wasm_bindgenでErrorの継承を行う方法が分からなかったので,エラー自体はJavascript側で定義しwasm_bindgenでRust側にインポートします.
簡単なものですがプログラムはこちらにあります.
エラーの定義
エラーは以下で定義しています.
#[derive(thiserror::Error, Debug)]
#[error("too small x for log(x) x:{0}. (expected x > 0.01)")]
pub struct LogSmallError(pub f64);
#[derive(thiserror::Error, Debug)]
#[error("too large x for exp(x) x:{0}. (expected x < 10)")]
pub struct ExpLargeError(pub f64);
エラーをRust側にインポートする
Javascript側でエラーのクラスを定義します.ファイルはwasm_bindgenが読み込める場所ならどこにあっても大丈夫ですが,今回はpkg
ディレクトリの中に作成します.エラーメッセージのフォーマットなどはRust部分で定義しているので,ここでは必要最低限の定義だけしています.
export class MathError extends Error {
constructor(...params: any[]) {
super(...params)
this.name = "MathError"
}
}
export class LogSmallError extends MathError {
constructor(...params: any[]) {
super(...params)
this.name = "LogSmallError"
}
}
export class ExpLargeError extends MathError {
constructor(...params: any[]) {
super(...params)
this.name = "ExpLargeError"
}
}
wasm_bindgenではexten c
の中にJavascriptからインポートする関数やメソッドのシグネチャを記述することで,Rust内で利用できるようにします.Javascript側で定義したエラーを簡単のためにerror.rs
のエラーと同じ名前としているので,lib.rs
からerror.rs
の同名のエラーを利用する場合はcrate::error::LogSmallError
のようにします.
マクロの引数としてmodule
を指定することで,wasm-packが出力するJavascriptファイルにimport文を含めることができます.ここではmath_lib/pkg
ディレクトリまでのパスを@math_lib
というエイリアスとしています.各エラーはextends
で継承するベースクラスを明示していますが,今回は各エラー同士を変換することはないため省いても大丈夫です.
#[wasm_bindgen(module = "@math_lib/wasm_error")]
extern "C" {
#[wasm_bindgen]
pub type MathError;
#[wasm_bindgen(extends = MathError)]
pub type LogSmallError;
#[wasm_bindgen(constructor)]
pub fn new(msg: String) -> LogSmallError;
#[wasm_bindgen(extends = MathError)]
pub type ExpLargeError;
#[wasm_bindgen(constructor)]
pub fn new(msg: String) -> ExpLargeError;
}
Rust側でエラーを返しJavascriptでキャッチする
wasm_bindgenではエラーとしてwasm_bindgen::JsValue
を返す必要があるので,Fromトレイトを実装します.Fromトレイトを実装することで,定義した各エラーからResult<T, JsValue>
に?
で返すことができます.
impl From<crate::error::LogSmallError> for JsValue {
fn from(err: crate::error::LogSmallError) -> JsValue {
LogSmallError::new(err.to_string()).into()
}
}
impl From<crate::error::ExpLargeError> for JsValue {
fn from(err: crate::error::ExpLargeError) -> JsValue {
ExpLargeError::new(err.to_string()).into()
}
}
通常のように関数を定義します.
fn log(x: f64) -> Result<f64, crate::error::LogSmallError> {
if x < 0.01 {
return Err(crate::error::LogSmallError(x));
} else {
return Ok(x.ln());
}
}
fn exp(x: f64) -> Result<f64, crate::error::ExpLargeError> {
if x > 10.0 {
return Err(crate::error::ExpLargeError(x));
} else {
return Ok(x.exp());
}
}
#[wasm_bindgen]
pub fn wasm_log(x: f64) -> Result<f64, JsValue> {
Ok(log(x)?)
}
#[wasm_bindgen]
pub fn wasm_exp(x: f64) -> Result<f64, JsValue> {
Ok(exp(x)?)
}
以上の関数をJavascript側から呼んで例外をキャッチすると以下のようになります,
import init, {wasm_log, wasm_exp} from "@math_lib/math_lib";
import {MathError, LogSmallError, ExpLargeError} from "@math_lib/wasm_error"
import wasm_path from "@math_lib/math_lib_bg.wasm?url";
await init(wasm_path);
try{
wasm_log(0.0);
} catch(e) {
console.log("---- wasm_log ----");
console.log(e);
console.log(`e is instance of MathError: ${e instanceof MathError}`);
console.log(`e is instance of LogSmallError: ${e instanceof LogSmallError}`);
console.log(`e is instance of ExpLargeError: ${e instanceof ExpLargeError}`);
}
try{
wasm_exp(1000);
} catch(e) {
console.log("---- wasm_exp ----");
console.log(e);
console.log(`e is instance of ExpLargeError: ${e instanceof ExpLargeError}`);
}
---- wasm_log ----
main.ts:11 LogSmallError: too small x for log(x) x:0. (expected x > 0.01)
at imports.wbg.__wbg_new_4b9dc3046e1dba61 (math_lib.js?t=1639234100779:132)
at math_lib_bg.wasm:0xa344
at math_lib_bg.wasm:0x9d4b
at math_lib_bg.wasm:0xae8a
at wasm_log (math_lib.js?t=1639234100779:54)
at main.ts:8
main.ts:12 e is instance of MathError: true
main.ts:13 e is instance of LogSmallError: true
main.ts:14 e is instance of ExpLargeError: false
main.ts:20 ---- wasm_exp ----
main.ts:21 ExpLargeError: too large x for exp(x) x:1000. (expected x < 10)
at imports.wbg.__wbg_new_fbc4d6542b13d2e9 (math_lib.js?t=1639234100779:140)
at math_lib_bg.wasm:0xa3ad
at math_lib_bg.wasm:0x9dfe
at math_lib_bg.wasm:0xad3a
at wasm_exp (math_lib.js?t=1639234100779:63)
at main.ts:18
main.ts:22 e is instance of ExpLargeError: true
スーパークラスも含めて,正しくinstance of
で判別できています,