* 使用 `fail` 方法来抛出异常。仅在捕捉到异常时使用 `raise` 来重新抛出异常(因为没有失败,所以只是显式地有目的性地抛出一个异常)
~~~
begin
fail 'Oops';
rescue => error
raise if error.message != 'Oops'
end
~~~
* 如果 `fail/raise` 只有两个参数,无需显性指定 `RuntimeError`。
~~~
# 差
fail RuntimeError, 'message'
# 好——默认就是 RuntimeError
fail 'message'
~~~
* 将异常类和消息作为参数给 `fail/raise` ,而不是异常类的的实例。
~~~
# 差
fail SomeException.new('message')
# 无法使用 `fail SomeException.new('message'), backtrace`.
# 好
fail SomeException, 'message'
# 可以使用 `fail SomeException, 'message', backtrace`.
~~~
* 永远不要从 `ensure` 区块返回。如果你显式地从 `ensure` 区块中的一个方法返回,那么这方法会如同没有异常般的返回。实际上,异常会被默默丢掉。
~~~
def foo
begin
fail
ensure
return 'very bad idea'
end
end
~~~
* 尽可能使用隐式的 `begin` 区块。
~~~
# 差
def foo
begin
# 此处放主要逻辑
rescue
# 错误处理放在此处
end
end
# 好
def foo
# 此处放主要逻辑
rescue
# 错误处理放在此处
end
~~~
* 通过 _contingency_ 方法 (一个由 Avdi Grimm 创造的词) 来减少 `begin` 区块的使用。
~~~
# 差
begin
something_that_might_fail
rescue IOError
# 处理 IOError
end
begin
something_else_that_might_fail
rescue IOError
# 处理 IOError
end
# 好
def with_io_error_handling
yield
rescue IOError
# 处理 IOError
end
with_io_error_handling { something_that_might_fail }
with_io_error_handling { something_else_that_might_fail }
~~~
* 不要抑制异常。
~~~
begin
# 这里发生了一个异常
rescue SomeError
# 拯救子句完全没有做事
end
# 差
do_something rescue nil
~~~
* 避免使用 `rescue` 的修饰符形式。
~~~
# 差 - 这捕捉了所有的 StandardError 异常。
do_something rescue nil
~~~
* 不要为了控制流程而使用异常。
~~~
# 差
begin
n / d
rescue ZeroDivisionError
puts 'Cannot divide by 0!'
end
# 好
if d.zero?
puts 'Cannot divide by 0!'
else
n / d
end
~~~
* 避免救援 `Exception` 类别。这会把信号困住,并呼叫 `exit`,导致你需要 `kill -9` 进程。
~~~
# 差
begin
# 呼叫 exit 及杀掉信号会被捕捉(除了 kill -9)
exit
rescue Exception
puts "you didn't really want to exit, right?"
# 异常处理
end
# 好
begin
# 一个不明确的 rescue 子句捕捉的是 StandardError,
# 而不是许多编程者所设想的 Exception。
rescue => e
# 异常处理
end
# 也好
begin
# 这里发生一个异常
rescue StandardError => e
# 异常处理
end
~~~
* 把较具体的异常放在救援串连的较上层,不然它们永远不会被拯救。
~~~
# 差
begin
# 一些代码
rescue Exception => e
# 一些处理
rescue StandardError => e
# 一些处理
end
# 好
begin
# 一些代码
rescue StandardError => e
# 一些处理
rescue Exception => e
# 一些处理
end
~~~
* 在 `ensure` 区块中释放你的程式的外部资源。
~~~
f = File.open('testfile')
begin
# .. 处理
rescue
# .. 错误处理
ensure
f.close unless f.nil?
end
~~~
* 倾向使用标准库的异常类而不是导入新的异常类。