引数なしの rescue は StandardError の子孫しか捕まえない
クラス名を指定しない rescue は、暗黙のうちに StandardError とそのサブクラスだけを捕まえます。自作の例外クラスを Exception を直接継承させて定義すると、rescue で捕まえられずプログラムが止まります。
エラーメッセージの読み方
main.rb:4:in `<main>': custom failure (MyError)
main.rb- ファイル名
4- 行番号 — 実際にクラッシュした行
<main>- 発生場所 — <main> ならトップレベル、メソッド名ならそのメソッドの中
MyError- 例外クラス — 何が起きたか。ここを検索するのが最短です
custom failure- 詳細メッセージ — どの値が問題だったか
このエラーが出る典型パターン
パターン1
1 class MyError < Exception ; end ^ 2 3 begin 4 raise MyError, "custom failure" 5 rescue 6 puts "caught" 7 end
引数なしのrescueはStandardErrorの子孫しか捕まえません。Exceptionを直接継承させると素通りしてプログラムが止まります。
直し方: Exception を StandardError にします。
パターン2
1 class ValidationError < Exception ; end ^ 2 3 begin 4 raise ValidationError, "invalid input" 5 rescue 6 puts "caught" 7 end
自作の例外クラスはStandardErrorを継承させるのが基本です。Exceptionを継承させるのはRuby自身が使う特別な場合に限られます。
直し方: Exception を StandardError にします。
パターン3
1 class TimeoutFailure < Exception ; end ^ 2 3 begin 4 raise TimeoutFailure, "took too long" 5 rescue 6 puts "caught" 7 end
「rescueは何でも捕まえてくれる」という思い込みは、継承元のクラスを間違えると崩れます。
直し方: Exception を StandardError にします。
パターン4
1 class ParseFailure < Exception ; end ^ 2 3 begin 4 raise ParseFailure, "bad format" 5 rescue 6 puts "caught" 7 end
継承元をExceptionのままにすると、rescueで捕まえられずプログラムがその場で終了します。
直し方: Exception を StandardError にします。
パターン5
1 class AuthFailure < Exception ; end ^ 2 3 begin 4 raise AuthFailure, "denied" 5 rescue 6 puts "caught" 7 end
自作の例外を安全にrescueで扱いたい場合は、必ずStandardErrorを継承元にしてください。
直し方: Exception を StandardError にします。
よくある誤解
「rescueは投げられた例外を種類を問わず何でも捕まえてくれるはず」という思い込みは誤りです。自作の例外クラスは StandardError を継承させないと、素の rescue の対象から外れてしまいます。
まとめ
引数なしの rescue は StandardError の子孫しか捕まえないは中級でつまずきやすい項目です。上の5パターンを実際に手で直すと、エラーメッセージのどこを読めばよいかが掴めます。