例外處理(Exception Handling)是指在進行運算時,出現例外(Exception)的情況對應的處理。
Exception
在Ruby中,例外和error都是用Exception
這個class,大概定義了30種左右不同的subclasses,像是打錯method名字,導致的NoMethodError
等等,當產生Exception時,如果沒有東西去處理它的話,整個程式會停下來,例如下面的程式碼,最後一行code,會因為前面的Exception,永遠執行不到:
x = "123"
puts "x: #{x}"
x.wrong_method # error at here
puts "You can't see me"
=> x: 123
Traceback (most recent call last):
test.rb:4:in `<main>': undefined method `wrong_method' for "123":String (NoMethodError)
那麼,接下來我們就來看看,在Ruby,可以怎麼處理Exception吧!
begin / rescue
基本的結構是 begin...rescue
這樣的code block,begin
裡面是你要執行的code,rescue
則是當begin
中發生任何exception時,會執行的地方。
begin
# Execute code(may raise exception)
rescue
# Code will be executed when error
end
讓我們實際來看看效果,當Error發生時,程式不會停住,會直接跳去執行rescue
內的程式:
begin
puts "Code is executing"
nil + 1 # NoMethodError
puts "Never excute"
rescue
puts "Your code broken la"
end
=>
Code is executing
Your code broken la
Assign Exception object
Exception發生所產生的Exception objects,其實和一般的ruby object一樣,裡面記錄了一些關於這個例外的資訊,我們可以利用assigned變數的方式拿到更多關於這個Exception的詳細資訊。
begin
nil + 1
rescue => e
puts "Exception Class: #{ e.class.name }"
puts "Exception Message: #{ e.message }"
puts "Exception Backtrace: #{ e.backtrace }"
end
=>
Exception Class: NoMethodError
Exception Message: undefined method `+' for nil:NilClass
Exception Backtrace: ["test.rb:2:in `<main>'"]
Rescue Specific Exception
rescue
預設會rescues所有的StandardError(StandardError包括哪些subclass,可以參考ruby的文件),那麼當想要rescue特定的exception時,只要在rescue加上class的名字,就可以針對特定的exception作處理了。
# NoMethodError
begin
nil + 1
rescue ZeroDivisionError => e
puts "ZeroDivisionError: #{ e.message }"
rescue NoMethodError => e
puts "NoMethodError: #{ e.message }"
end
=> NoMethodError: undefined method `+' for nil:NilClass
# ZeroDivisionError
begin
1 / 0
rescue ZeroDivisionError => e
puts "ZeroDivisionError: #{ e.message }"
rescue NoMethodError => e
puts "NoMethodError: #{ e.message }"
end
=> ZeroDivisionError: divided by 0
# Handle Other
begin
1 / nil
rescue ZeroDivisionError => e
puts "ZeroDivisionError: #{ e.message }"
rescue NoMethodError => e
puts "NoMethodError: #{ e.message }"
rescue => e
puts "Other: #{ e.class.name }"
end
=> Other: TypeError
raise
事實上,我們也可以自己利用 raise
去觸發Exception,使用 raise
,預設會產生一個RuntimeError,當然也可以自己指定產生的Exception是哪個subclass。
# default RuntimeError
begin
puts "raise test start!"
raise "wahaha, you got error"
rescue => e
puts "Your raise: #{ e.class.name }"
puts "Error message: #{ e.message }"
end
=>
raise test start!
Your raise: RuntimeError
Error message: wahaha, you got error
# raise specific error
begin
puts "raise test start!"
raise ArgumentError, "wahaha, you got error"
rescue => e
puts "Your raise: #{ e.class.name }"
puts "Error message: #{ e.message }"
end
=>
raise test start!
Your raise: ArgumentError
Error message: wahaha, you got error
ensure
最後,還有一個 ensure
,在 ensure
裡面的code,是不管有沒有Exception發生,都一定會執行到
# Exception occur
begin
1 / 0
rescue => e
puts "Error: #{e.message}"
ensure
puts "You always see me."
end
=>
Error: divided by 0
You always see me.
# Non exception occur
begin
1 / 1
rescue => e
puts "Error: #{e.message}"
ensure
puts "You always see me."
end
=>
You always see me.
總結
根據上面的討論,我們來總結一下ruby的例外處理,大致可以用以下的結構來說明:
begin
# The code may cause error
rescue
# When Exception occur(default is StandardError)
ensure
# The code always execute
end
參考資料
例外處理wiki
How to Handle Exceptions in Ruby
A Beginner's Guide to Exceptions in Ruby
How to Rescue Exceptions in Ruby
Rails 實戰聖經-程式語言6: 例外處理