C++ ときどき ごはん、わりとてぃーぶれいく☆

USAGI.NETWORKのなかのひとのブログ。主にC++。

wasm-bindgen で fetch する rustwasm 公式の example の async で JsFuture な run が実行時に失敗した場合にも安全に対応できるようにする方法のメモ

問題

  • この example の runrustwasm/wasm-bindgen のような、実装上GitHub APIで取得可能なポジトリーが実在する場合は問題ありません。
  • リポジトリーが存在しないパターンを run に与えて fetch させると async/JsFuture 処理系の都合か二度と run を使用できない .wasm が生成されてしまいます。
    • 実際にやってみると、一度非実在リポジトリーを叩いてエラーを出すと実在のリポジトリーを叩こうと叩くまいと run は二度と動作しなくなります。

問題は example の run の最後の2行:

    // Use serde to parse the JSON into a struct.
    let branch_info: Branch = json.into_serde().unwrap();

    // Send the `Branch` struct back to JS as an `Object`.
    Ok(JsValue::from_serde(&branch_info).unwrap())

解決方法

matchjsonErr<serde_json::error::Error>Err<JsValue> に射る:

 // 解決方法①段階
 match json.into_serde() as Result<Branch, serde_json::error::Error> {
 Ok(branch_info) => Ok(JsValue::from_serde(&branch_info).unwrap()),
 Err(e) => Err(JsValue::from_str(&format!("{}", e))),
 }

↑だとまだ json.into_serde()Ok だけど JsValue::from_serdeErr だった場合は死んでしまいますが、 GitHub API は実在しないリポジトリーに対しても JSON を返してはくれるので、さしあたりは問題にはなりません。そこも match すると↓:

 // 解決方法②段階
 match json.into_serde() as Result<Branch, serde_json::error::Error> {
  Ok(branch_info) => match JsValue::from_serde(&branch_info) {
   Ok(jsvalue) => Ok(jsvalue),
   Err(e) => Err(JsValue::from_str(&format!("{}", e))),
  },
  Err(e) => Err(JsValue::from_str(&format!("{}", e))),
 }

↑のそこはかとない多段 match のダサさを Resultmapmap_err で整理すると:

 // 解決方法③段階
 (json.into_serde() as Result<Branch, serde_json::error::Error>)
  .map(|branch_info| JsValue::from_serde(&branch_info).unwrap())
  .map_err(|e| JsValue::from_str(&format!("{}", e)))
  • (1. json.into_serde()): json.into_serde() := Ok<Branch>Err<serde_json::error::Error> です
  • (2. map): (a) が Ok の場合に mapfrom_serde の結果を unwrap := Ok<JsValue> または Err<serde_json::error::Error> です
  • (3. map_err): (a) または (b) の何れかが Err<serde_json::error::Error> の場合は map_errErr<JsValue> に変換されます

こうすると run の内部で Err<serde_json::error::Error> が発生しても Uncaught (in promise) missing filed `name` at line 1 column 104 のような console.error が吐かれるだけで、 run が使用不能には至らなくなります。

この他にも unwrap() により panic が発生する可能性がある部分はありますが、 GitHub が 404 になったり JSON レスポンスを廃止しない限りは事実上死には至らないです。学習用途ではなく実用する場合はより厳密に async/JsFuture の絡む内部で panic が発生しないように気にする必要はあります。