Exception 处理

异常在 begin/end 块中被捕获。

begin
  # code that might raise
rescue
  # handle exception
end

如果你在一个方法内部,除非你想限制捕获异常的范围,否则不需要使用 beginend

def my_method
  # ...
rescue
  # ...
end

对于 classmoduleblock 也是如此。

[0, 1, 2].map do |i|
  10 / i
rescue ZeroDivisionError
  nil
end
#=> [nil, 10, 5]

你可以通过在 rescue 行的末尾使用 => 变量名 将异常分配给一个局部变量。

begin
  # ...
rescue => exception
  warn exception.message
  raise # re-raise the current exception
end

默认情况下,StandardError 及其子类会被捕获。你可以通过在 rescue 之后列出特定的异常类(及其子类)来捕获它们。

begin
  # ...
rescue ArgumentError, NameError
  # handle ArgumentError or NameError
end

你可以用不同的方式捕获不同类型的异常。

begin
  # ...
rescue ArgumentError
  # handle ArgumentError
rescue NameError
  # handle NameError
rescue
  # handle any StandardError
end

异常从上到下与 rescue 部分匹配,并且只匹配一次。如果在 begin 部分引发了 ArgumentError,它将不会在 StandardError 部分中被处理。

你可以重试捕获的异常。

begin
  # ...
rescue
  # do something that may change the result of the begin block
  retry
end

执行将从 begin 块的开头恢复,因此请注意不要创建无限循环。

在 rescue 块内是 retry 唯一有效的位置,所有其他使用都会引发 SyntaxError。如果你想重试一个块迭代,请使用 redo。有关详细信息,请参阅 控制表达式

为了无论是否引发异常都始终运行一些代码,请使用 ensure

begin
  # ...
rescue
  # ...
ensure
  # this always runs BUT does not implicitly return the last evaluated statement.
end

当没有引发异常时,你也可以运行一些代码。

begin
  # ...
rescue
  # ...
else
  # this runs only when no exception was raised AND return the last evaluated statement
ensure
  # this always runs.
  # It is evaluated after the evaluation of either the `rescue` or the `else` block.
  # It will not return implicitly.
end

注意:如果没有在 ensure 块中显式使用 return,则 begin/end 块将返回进入 ‘ensure’ 块之前最后求值的语句。