サーチ…


基本的なエラー処理ブロック

2つの数値を分ける関数を作ってみましょう。それはその入力について非常に信頼しています:

def divide(x, y)
  return x/y
end

これは多くの入力に対してうまく動作します:

> puts divide(10, 2)
5

すべてではありません

> puts divide(10, 0)
ZeroDivisionError: divided by 0

> puts divide(10, 'a')
TypeError: String can't be coerced into Fixnum

begin... endブロックで危険な除算操作をラップしてエラーをチェックし、 rescue句を使用してメッセージを出力し、問題があればnilを返すことで関数を書き直すことができます。

def divide(x, y)
  begin
    return x/y
  rescue
    puts "There was an error"
    return nil
  end
end

> puts divide(10, 0)
There was an error

> puts divide(10, 'a')
There was an error

エラーの保存

rescue句でエラーを使用する場合は、エラーを保存できます

def divide(x, y)
  begin
    x/y
  rescue => e
    puts "There was a %s (%s)" % [e.class, e.message]
    puts e.backtrace
  end
end

> divide(10, 0)
There was a ZeroDivisionError (divided by 0)
       from (irb):10:in `/'
       from (irb):10
       from /Users/username/.rbenv/versions/2.3.1/bin/irb:11:in `<main>'

> divide(10, 'a')
There was a TypeError (String can't be coerced into Fixnum)
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/workspace.rb:87:in `eval'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/workspace.rb:87:in `evaluate'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/context.rb:380:in `evaluate'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb.rb:489:in `block (2 levels) in eval_input'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb.rb:623:in `signal_status'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb.rb:486:in `block in eval_input'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/ruby-lex.rb:246:in `block (2 levels) in each_top_level_statement'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/ruby-lex.rb:232:in `loop'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/ruby-lex.rb:232:in `block in each_top_level_statement'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/ruby-lex.rb:231:in `catch'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/ruby-lex.rb:231:in `each_top_level_statement'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb.rb:485:in `eval_input'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb.rb:395:in `block in start'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb.rb:394:in `catch'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb.rb:394:in `start'
/Users/username/.rbenv/versions/2.3.1/bin/irb:11:in `<main>'

さまざまなエラーのチェック

エラーの種類に応じて異なる処理を実行する場合は、引数として異なるエラータイプを持つ複数のrescue句を使用します。

def divide(x, y)
  begin
    return x/y
  rescue ZeroDivisionError
    puts "Don't divide by zero!"
    return nil
  rescue TypeError
    puts "Division only works on numbers!"
    return nil
  end
end

> divide(10, 0)
Don't divide by zero!

> divide(10, 'a')
Division only works on numbers!

rescueブロックで使用するためにエラーを保存する場合は、次のようにします。

rescue ZeroDivisionError => e

他のrescue句に指定されていない型のエラーをキャッチするには、引数のないrescue句を使用します。

def divide(x, y)
  begin
    return x/y
  rescue ZeroDivisionError
    puts "Don't divide by zero!"
    return nil
  rescue TypeError
    puts "Division only works on numbers!"
    return nil
  rescue => e
    puts "Don't do that (%s)" % [e.class]
    return nil
  end
end

> divide(nil, 2)
Don't do that (NoMethodError)

この場合、 nilを2で除算しようとすると、 ZeroDivisionErrorまたはTypeErrorではないので、デフォルトのrescue句で処理されます。このrescue句は、 NoMethodErrorあることを知らせるメッセージを出力します。

再試行

rescue句では、 retryを使用して、おそらくエラーの原因となった状況を変更した後にbegin句を再度実行できます。

def divide(x, y)
  begin
    puts "About to divide..."
    return x/y
  rescue ZeroDivisionError
    puts "Don't divide by zero!"
    y = 1
    retry
  rescue TypeError
    puts "Division only works on numbers!"
    return nil
  rescue => e
    puts "Don't do that (%s)" % [e.class]
    return nil
  end
end

TypeErrorを引き起こすことがわかっているパラメータを渡すと、 begin句が実行され(ここでは「分割する」と表示してフラグを立てる)、以前と同じようにエラーがキャッチされ、 nilが返されます。

> divide(10, 'a')
About to divide...
Division only works on numbers!
 => nil

しかし、 ZeroDivisionErrorを引き起こすパラメータを渡すと、 begin句が実行され、エラーがキャッチされ、除数が0から1に変更された後、 retrybeginブロックが(上から)異なるy 。 2回目にはエラーはなく、この関数は値を返します。

> divide(10, 0)
About to divide...     # First time, 10 ÷ 0
Don't divide by zero!
About to divide...     # Second time 10 ÷ 1
=> 10

エラーが発生していないかどうかの確認

エラーが発生しない場合に実行されるコードには、 else節を使用できます。

def divide(x, y)
  begin
    z = x/y
  rescue ZeroDivisionError
    puts "Don't divide by zero!"
  rescue TypeError
    puts "Division only works on numbers!"
    return nil
  rescue => e
    puts "Don't do that (%s)" % [e.class]
    return nil
  else
    puts "This code will run if there is no error."
    return z
  end
end

else節は、 rescue句の1つに制御を渡すエラーがある場合は実行されません。

> divide(10,0)
Don't divide by zero!
=> nil

しかし、エラーが発生しなければ、 else節が実行されます:

> divide(10,2)
This code will run if there is no error.
=> 5

begin節から戻るelse節は実行されないことに注意してください

def divide(x, y)
  begin
    z = x/y
    return z                 # Will keep the else clause from running!
  rescue ZeroDivisionError
    puts "Don't divide by zero!"
  else
    puts "This code will run if there is no error."
    return z
  end
end

> divide(10,2)
=> 5

常に実行する必要があるコード

常に実行したいコードがある場合は、 ensure句を使用してください。

def divide(x, y)
  begin
    z = x/y
    return z
  rescue ZeroDivisionError
    puts "Don't divide by zero!"
  rescue TypeError
    puts "Division only works on numbers!"
    return nil
  rescue => e
    puts "Don't do that (%s)" % [e.class]
    return nil
  ensure
    puts "This code ALWAYS runs."
  end
end

エラーが発生した場合、 ensure句が実行されます。

> divide(10, 0)
Don't divide by zero!   # rescue clause
This code ALWAYS runs.   # ensure clause
=> nil

エラーがない場合:

> divide(10, 2)
This code ALWAYS runs.   # ensure clause
=> 5

確実句は、たとえばファイルが閉じられていることを確認したい場合に便利です。

else節とは異なり、 ensure begin節またはrescue節が値を返す前に実行される ensure注意してください。場合はensure句がありreturn上書きしますreturn 、他の条項の価値を!



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow