例外とは?何かがうまくいかなかったことを知らせる
例外とは、実行中に何かがうまくいかなかったことを知らせるシグナルです。通常のフローを中断し、あるコードがそれを捕まえて処理するまで上へ伝播します。
コードが先へ進めない状況、すなわちファイルの欠落、不正な入力、失敗したネットワーク呼び出しに出くわすと、例外を送出(throw)できます。それはコールスタックを巻き戻し、通常の実行を飛ばして、handlerがそれを捕まえるまで進みます。例外はハッピーパスとエラー処理を分けますが、捕まえられない例外はプログラムを終了させます。
例外がどう流れるか
例外を送出すると通常の実行が止まり、対応するhandler(try/catchやそれに相当するもの)を探してコールスタックを上へたどります。見つかれば制御はそこへ飛び、見つからなければ例外は最上部に達し、エラーとstack traceとともにプログラムをクラッシュさせます。
例外を使うとき
- 通常のフローの外にある、真に例外的な状況。
- 直近ではなく遠くの呼び出し側が処理すべきエラー。
- エラー値を返すとコードが散らかる状況。
- 黙って無視されてはならない失敗。
例外 vs エラー値
一部の言語は例外を好み、他(GoやRustなど)は処理を明示的にするためエラーを値として返します。例外はハッピーパスを綺麗に保ちますが、どこで失敗が起きるかを隠し得ます。エラー値は明示的ですがより冗長です。どちらも有効なスタイルです。
stack trace
捕まえられない例外はstack traceを印字します。エラーに至った呼び出しの連鎖です。それはデバッグにとって最も有用な成果物で、失敗がどこでどう起きたかを正確に示します。stack traceをうまく読むことは中核的なスキルです。
手短な例
JSON.parse("not json") を呼ぶと例外が送出されます。それをtry/catchで包めば、プログラム全体をクラッシュさせる代わりに、不正な入力を優雅に処理できます。
CIにおける例外
テスト内の捕まえられない例外は実行をfailさせ、原因への最初の手がかりであるstack traceを印字します。そうした失敗のほとんどは修正すべき決定論的なバグですが、一部は一時的なインフラ(切れた接続)に由来します。Latchkeyは一時的なケースを自動でリトライしつつ、本物の例外は見えたままにします。
重要なポイント
- 例外はエラーを知らせ、handlerが捕まえるまでスタックを巻き戻す。
- 捕まえられない例外はプログラムをクラッシュさせ、デバッグ用のstack traceを印字する。
- 例外とエラー値は、失敗を扱う2つの有効なスタイルである。