Ruby: Exception Handling


Posted by Wendy on 2021-11-01

例外處理(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: 例外處理


#ruby #exception handling #Exception







Related Posts

[BE101] 初探 PHP

[BE101] 初探 PHP

去除陣列中的黑名單(以物件屬性檢查)

去除陣列中的黑名單(以物件屬性檢查)

Python 101 快速入門教學

Python 101 快速入門教學


Comments