# Rust Workers の panic、もうインスタンスを道連れにしないよ！

やっほー、しぃちゃんだよ！ 今日はちょっとマニアックだけど、とっても大事な信頼性のお話だよ。Rust で書いた Workers が panic しても、もう道連れにしなくなるんだって！ わくわくしちゃうね。

## なにが発表されたの？

Cloudflare の Blog が、[Rust Workers](https://developers.cloudflare.com/workers/languages/rust/) の信頼性をぐっと高める仕組みを発表したの。テーマは「panic と abort からの回復」。Rust を WebAssembly (Wasm) に変換する土台になっている [wasm-bindgen](https://github.com/wasm-bindgen/wasm-bindgen) に手を入れて、panic が起きても WebAssembly インスタンスを壊さずに立て直せるようにしたんだって。

## 今までどうだったの？

これまで Rust Workers で panic が起きると、けっこう深刻だったの。`wasm32-unknown-unknown` ターゲットは既定で `panic=abort` になっていて、panic するといきなり `unreachable` 命令でトラップして、JavaScript 側に `WebAssembly.RuntimeError` として抜けちゃう。しかもインスタンスの状態が壊れて、元に戻せなくなっちゃうの。

一番こわいのは、1 つのリクエストの失敗が、同じインスタンスで動いている別のリクエストや、新しく来たリクエストにまで連鎖しちゃうこと。Cloudflare は最初、JavaScript の Proxy でラップして失敗後にモジュールを作り直す方法で対処していたんだけど、これだと再初期化のたびにメモリ上の状態が消えちゃうの。Durable Objects みたいに状態を持つワークロードにはつらいよね。

## これで何が変わるの？

新しい仕組みでは、panic がちゃんと JavaScript の例外 (PanicError) として表に出てくるようになったの。だから catch して扱えるし、async な export なら返り値の Promise が PanicError で reject されるよ。

しかも Rust のデストラクタ(後片付け)が正しく走って、WebAssembly インスタンスはそのまま使い続けられるの！ つまりメモリ上の状態を失わずに回復できるってこと。状態を持つ Durable Objects にとっては、特にうれしいポイントだね。

## 深く潜ってみよう

カギになったのは、2023 年に主要エンジンで広くサポートされた WebAssembly Exception Handling の仕組み。これを使って `wasm32-unknown-unknown` でも `panic=unwind` を実現したの。

ビルドはこんな感じで、標準ライブラリごと unwind 対応で組み直すよ:

```
RUSTFLAGS='-Cpanic=unwind' cargo build -Zbuild-std
```

unwind するとき、コンパイラは WebAssembly の try / catch ブロックを生成して、途中で panic してもデストラクタが走るようにするの。イメージはこんな感じ:

```
try
  call imported_func
catch_all
  call drop_b
  call drop_a
  rethrow
end
```

wasm-bindgen 側もいろいろ改修が必要だったんだって。Walrus っていう WebAssembly パーサに try / catch を理解させたり、export を `extern "C-unwind"` にして Rust と JavaScript の境界を越えて unwind できるようにしたり。境界で panic をキャッチして PanicError にして返す仕組みも入ったよ。詳しい仕組みは [Catching Panics のガイド](https://wasm-bindgen.github.io/wasm-bindgen/reference/catch-unwind.html) にまとまってるの。

それでも abort は残るの。メモリ不足 (OOM) みたいな abort は unwind できないから状態は回復できないんだけど、検知して次の処理への連鎖を防ぐことはできるようにしたよ。Exception.Tag を使って「回復できる panic」と「回復できない abort」を見分けて、初期化時に付けられる `set_on_abort` フックや、深く入り組んだ呼び出しのための再入ガードを用意したんだって。こっちは [Handling Aborts のガイド](https://wasm-bindgen.github.io/wasm-bindgen/reference/handling-aborts.html) に載ってるよ。

使う側としては、workers-rs 0.8.0 以降でビルド時に `--panic-unwind` フラグを付けると有効になるの([設定の手順はここ](https://github.com/cloudflare/workers-rs?tab=readme-ov-file#panic-recovery-with---panic-unwind))。ただし今は nightly の実験的なターゲットが必要で、安定版に持っていく作業は進行中。unwind と相性の悪い参照をキャプチャするクロージャは、`Closure::new_aborting` を使うなどの工夫がいるよ。

## まとめ

- Rust Workers の panic が、インスタンス全体を壊さず JavaScript の PanicError として回復できるようになったよ
- WebAssembly Exception Handling を使って `panic=unwind` を実現、デストラクタも正しく走ってメモリ状態を維持できるの
- abort(OOM など)は Exception.Tag で見分けて連鎖を防ぐ設計。`set_on_abort` フックも追加されたよ
- 使うには workers-rs 0.8.0 以降で `--panic-unwind`。今は nightly 前提で、安定版化は進行中
- Rust で Workers や Durable Objects を書いていて、信頼性を突き詰めたい人にドンピシャな話だよ！
