Ruby の StandardError でどういう間違いをすると, どの例外が発生するのかのメモ


Ruby の例外クラス Exception のサブクラス StandardError で, どういう失敗をすると StandardError のどのサブクラス (例外) が発生するのかのメモです.

Ruby のバージョンは 2.5.1 になります.

ArgumentError

メソッドを呼び出す際に, 間違った引数を渡すと発生します.

例 1) 間違った引数の数を渡した時:

'1'.to_i(1, 2) rescue $!
  # => #<ArgumentError: wrong number of arguments (given 2, expected 0..1)>

例 2) 受け入れられない引数を渡した時:

'1'.to_i 1 rescue $! # => #<ArgumentError: invalid radix 1>

UncaughtThrowError

catchthrow で, throwcatch に存在しないタグを渡すと発生します:

catch(1) { throw 2 } rescue $! # => #<UncaughtThrowError: uncaught throw 2>

EncodingError

EncodingError はエンコードエラーのベースクラスです.

例 1) 存在しないコードコンバータでエンコードしようとした時:

'hello'.encode 'CUSTOM_ENCODING' rescue $!
  # => #<Encoding::ConverterNotFoundError: code converter not found (UTF-8 to CUSTOM_ENCODING)>

FiberError

Fiber.new で作った Fiber を Fiber#resume で再開した後に, もう一度 Fiber#resume で再開しようとすると発生します:

fiber = Fiber.new {}
fiber.resume
fiber.resume rescue $! # => #<FiberError: dead fiber called>

IOError

IO 操作の失敗時に発生します.

例 1) 読み込み専用モードの File オブジェクトに書き込みをしようとすると発生します:

File.open('example.txt') do |f|
  f.write('Hello World') rescue $! # => #<IOError: not opened for writing>
end

例 2) IO#close でストリームを閉じた後に, 読み込みをしようとすると発生します:

File.open('example.txt') do |f|
  f.close
  f.read rescue $! # => #<IOError: closed stream>
end

EOFError

ファイルの終わりに達してから, なんらかの IO 操作をすると発生します:

File.open('example.txt') do |f|
  f.read
  f.readline rescue $! # => #<EOFError: end of file reached>
end

IndexError

無効なインデックスで参照すると発生します:

[:a].fetch 1 rescue $!
  # => #<IndexError: index 1 outside of array bounds: -1...1>

KeyError

指定されたキーが見つからないと発生します:

{a: :a}.fetch :b rescue $! # => #<KeyError: key not found: :b>

StopIteration

Enumerator#next 呼び出し時に次の要素がないと発生します:

enum = [:a].each
enum.next
enum.next rescue $! # => #<StopIteration: iteration reached an end>

LocalJumpError

要求通りに yield できないと発生します:

def foo; yield end
foo rescue $! # => #<LocalJumpError: no block given (yield)>

def bar; proc { return } end
bar.call rescue $! # => #<LocalJumpError: unexpected return>

NameError

与えられた名前が無効だったり, 定義されていないと発生します:

foo rescue $!
  # => #<NameError: undefined local variable or method `foo' for main:Object>

Symbol.const_set :a, :a rescue $! # => #<NameError: wrong constant name a>

Symbol::A rescue $! # => #<NameError: uninitialized constant Symbol::A>

NoMethodError

レシーバに定義されておらず, method_missing でも処理されないメソッドを呼び出すと発生します:

'hello'.pascalcase rescue $!
  # => #<NoMethodError: undefined method `pascalcase' for "hello":String>

RangeError

与えられた数が範囲外だと発生します:

[:a, :b].drop 1_000**10 rescue $!
  # => #<RangeError: bignum too big to convert into `long'>

FloatDomainError

Float::INFINITYFloat::NAN といった特殊な値を, その値に対応していない IntegerRational に変換しようとすると発生します:

Float::INFINITY.to_i rescue $! # => #<FloatDomainError: Infinity>
Float::INFINITY.to_r rescue $! # => #<FloatDomainError: Infinity>
Float::NAN.to_i rescue $!      # => #<FloatDomainError: NaN>
Float::NAN.to_r rescue $!      # => #<FloatDomainError: NaN>

RegexpError

Regexp.new に無効な正規表現文字列を渡すと発生します:

Regexp.new '+' rescue $!
  # => #<RegexpError: target of repeat operator is not specified: /+/>
Regexp.new '*' rescue $!
  # => #<RegexpError: target of repeat operator is not specified: /*/>
Regexp.new '?' rescue $!
  # => #<RegexpError: target of repeat operator is not specified: /?/>
Regexp.new '\1' rescue $!
  # => #<RegexpError: invalid backref number/name: /\1/>
Regexp.new '\k<a>' rescue $!
  # => #<RegexpError: undefined name <a> reference: /\k<a>/>
Regexp.new '\g<a>' rescue $!
  # => #<RegexpError: undefined name <a> reference: /\g<a>/>

RuntimeError

一般的なエラーの例外クラスで Kernel#raiseException クラスを何も指定しないで呼び出すと発生します:

raise 'a generic error' rescue $! # => #<RuntimeError: a generic error>

FrozenError

フローズンオブジェクトを変更しようとした時に発生します:

[].freeze << :a rescue $! # => #<FrozenError: can't modify frozen Array>
{}.freeze.merge!({a: :a}) rescue $! # => #<FrozenError: can't modify frozen Hash>

SystemCallError

SystemCallError は全てのローレベルなプラットフォーム依存のエラーのベースクラスです.

現在のプラットフォームで利用可能なエラーは SystemCallError のサブクラスで, Errno モジュールに定義されています.

Errno.constants でその全てのエラーの定数リストを取得できます.

例 1) 存在しないファイルを開こうとした時:

File.open('example.txt') rescue $!
  # => #<Errno::ENOENT: No such file or directory @ rb_sysopen - example.txt>

例 2) パーミッションが拒否された時:

File.open('example.txt', 'w') rescue $!
  # => #<Errno::EACCES: Permission denied @ rb_sysopen - example.txt>

ThreadError

スレッドに対して無効な操作をすると発生します.

例 1) Thread.new をブロック無しで呼び出した時:

Thread.new rescue $! # => #<ThreadError: must be called with a block>

TypeError

予期したものと違うタイプのオブジェクトに遭遇した時に発生します:

1 + '1' rescue $! # => #<TypeError: String can't be coerced into Integer>

'1' + 1 rescue $!
  # => #<TypeError: no implicit conversion of Integer into String>

'Hello' + :World rescue $!
  # => #<TypeError: no implicit conversion of Symbol into String>

[:a, :b].fetch '0' rescue $!
  # => #<TypeError: no implicit conversion of String into Integer>

ZeroDivisionError

IntegerRationalInteger0 で割ろうとした時に発生します:

1 / 0 rescue $!    # => #<ZeroDivisionError: divided by 0>
1/1r / 0 rescue $! # => #<ZeroDivisionError: divided by 0>

まとめ

Ruby の StandardError の全てのサブクラスは rescue でキャッチできるので, rescuestatement modifier として使い, $! で発生した例外を参照しています.

StandardError はその名の通り, Ruby の標準的なエラーなので, よくお目にかかります.

自分でクラスを作ろうとする際, どんな例外クラスを発生させるべきか決める時に, StandardError のサブクラスのそれぞれの例外はどんな時に発生するのかということをあらかじめ知っておくと参考になると思ったり, もしくは, 何かコードを書いて実行した時に発生した例外が具体的にどういう意味を表しているのかということをより理解することができるようになると思いこのようなメモを書きました.

Ruby の StandardError とその親クラス Exception に関するより詳しい情報は次の URL から参照できます:

https://ruby-doc.org/core-2.5.1/Exception.html

また Ruby の例外をどのように処理するかという情報は次の URL から参照できます:

https://ruby-doc.org/core-2.5.1/doc/syntax/exceptions_rdoc.html