例外クラス / StandardError / RuntimeError
| 対応: | Ruby 1.8(2003) |
|---|
Rubyには用途別に分類された例外クラスが用意されています。独自の例外クラスを定義して使うこともできます。
構文
# 独自例外クラスを定義します。
class CustomError < StandardError
def initialize(msg = "デフォルトメッセージ")
super
end
end
# 独自例外を発生させます。
raise CustomError
# 以下は発展的な使い方です。
raise CustomError, "詳細なメッセージ"
raise CustomError.new("メッセージ")
# 例外クラスの階層を確認します。
puts error_class.ancestors
主要な例外クラス
| 例外クラス | 概要 |
|---|---|
| StandardError | 一般的なエラーの基底クラスです。独自例外はこれを継承します。 |
| RuntimeError | 『raise』にクラスを指定しない場合に発生するデフォルトの例外です。 |
| TypeError | 型が不正な場合に発生します(例:文字列と整数の加算)。 |
| ArgumentError | 引数の数や値が不正な場合に発生します。 |
| NameError | 未定義の変数・メソッド名を参照した場合に発生します。 |
| NoMethodError | 存在しないメソッドを呼び出した場合に発生します(NameError のサブクラス)。 |
| ZeroDivisionError | 整数をゼロで除算した場合に発生します。 |
| IOError | ファイルやストリーム操作に失敗した場合に発生します。 |
| Errno::ENOENT | ファイルやディレクトリが見つからない場合に発生します。 |
サンプルコード
sample_exception_class.rb
# 独自例外クラスを定義します。
class ValidationError < StandardError
attr_reader :field_name
def initialize(field_name, msg = nil)
@field_name = field_name
super(msg || "#{field_name}の値が不正です。")
end
end
class AgeError < ValidationError; end
# 独自例外を発生させて補足します。
def register_user(name, age)
raise ValidationError.new("名前"), "名前は必須です。" if name.empty?
raise AgeError.new("年齢"), "年齢は0以上を指定してください。" if age < 0
puts "#{name}(#{age}歳)を登録しました。"
end
begin
register_user("桐生一馬", 37)
register_user("", 20)
rescue AgeError => e
puts "年齢エラー: #{e.message}"
rescue ValidationError => e
puts "バリデーションエラー[#{e.field_name}]: #{e.message}"
end
ruby exception_class.rb 桐生一馬(37歳)を登録しました。 バリデーションエラー[名前]: 名前は必須です。
複数の独自例外クラスを定義してrescueで使い分けます。サブクラスを先に書かないと親クラスのrescueに吸収されてしまう点に注意してください。
sample_exception_class2.rb
# 例外クラスの階層を定義します。
class AppError < StandardError; end
class NetworkError < AppError; end
class TimeoutError < NetworkError; end
def fetch_data(url)
raise TimeoutError, "接続がタイムアウトしました: #{url}" if url.include?("slow")
raise NetworkError, "ネットワークに接続できません: #{url}" if url.include?("bad")
raise AppError, "アプリケーションエラーが発生しました。"
end
# サブクラスを先にrescueします(逆順だと親クラスに吸収されます)。
["https://slow.example.com", "https://bad.example.com", "https://example.com"].each do |url|
begin
fetch_data(url)
rescue TimeoutError => e
puts "タイムアウト: #{e.message}"
rescue NetworkError => e
puts "ネットワーク障害: #{e.message}"
rescue AppError => e
puts "アプリエラー: #{e.message}"
end
end
ruby exception_class2.rb タイムアウト: 接続がタイムアウトしました: https://slow.example.com ネットワーク障害: ネットワークに接続できません: https://bad.example.com アプリエラー: アプリケーションエラーが発生しました。
独自例外クラスで『message』メソッドをオーバーライドすると、エラーコードや追加情報をメッセージに組み込めます。
sample_exception_class3.rb
# messageメソッドをオーバーライドして書式付きメッセージを返します。
class ApiError < StandardError
attr_reader :code, :endpoint
def initialize(code, endpoint, msg = nil)
@code = code
@endpoint = endpoint
super(msg || "HTTPエラー")
end
def message
"[#{@code}] #{super} — #{@endpoint}"
end
end
begin
raise ApiError.new(404, "/api/characters/999")
rescue ApiError => e
puts e.message
puts "コード: #{e.code}"
puts "エンドポイント: #{e.endpoint}"
end
ruby exception_class3.rb [404] HTTPエラー — /api/characters/999 コード: 404 エンドポイント: /api/characters/999
『retry』と組み合わせて、ネットワーク系の例外を一定回数だけ再試行する実装例です。
sample_exception_class4.rb
class RetryableError < StandardError; end
MAX_RETRIES = 3
def unstable_operation(attempt)
raise RetryableError, "一時的なエラーです(試行 #{attempt})" if attempt < 3
"成功"
end
retries = 0
begin
result = unstable_operation(retries + 1)
puts result
rescue RetryableError => e
retries += 1
puts "リトライ #{retries}: #{e.message}"
retry if retries < MAX_RETRIES
puts "最大リトライ回数に達しました。"
end
ruby exception_class4.rb リトライ 1: 一時的なエラーです(試行 1) リトライ 2: 一時的なエラーです(試行 2) 成功
概要
Rubyの例外クラスは『Exception』を頂点とする階層構造になっています。通常は『StandardError』のサブクラスを使い、独自例外も『StandardError』を継承して定義します。
独自例外クラスを定義すると、エラーの種類を明確に区別でき、呼び出し側で適切に対処できます。『rescue Exception』と書くと『SignalException』や『SystemExit』など終了シグナルまで補足してしまい、『Ctrl+C』でプログラムを終了できなくなります。基本的に『rescue StandardError』か、特定のサブクラスを指定してください。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。