* 使用?`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
~~~
* 傾向使用標準庫的異常類而不是導入新的異常類。