iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 25
0
自我挑戰組

滿滿的紅寶石不拿嗎?-- 去吧!我把世界上的一切都放在那裡了! 系列 第 25

例外處理 <> 白鬍子登場!就連海軍大將都無法擊倒的那個男人!- 滿滿的紅寶石不拿嗎?

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20201003/20128363cDiBrBzRjS.jpg

[Day25] 就算是海軍本部又怎麼樣,艾斯,老爹來救你了!

這篇要介紹的是 Ruby 裡的例外處理(Rescue Exception)

每天都在 coding 的我遇到噴錯已經是家常便飯,而這些錯誤可能是環境、是語法、是邏輯矛盾等等各式各樣的問題所造成。

然而,工程師除了要解決碰到的每個錯誤,有時候甚至可以「未卜先知」*,如果在撰寫程式碼時,就已經先猜到可能會有錯,並告知程式在特定錯誤發生時該如何處理,就能防範於未然,省下噴錯後還得去找問題的時間。

*聽說厲害的工程師還會「通靈」!


什麼是例外處理?

簡單來說,當程式執行時出現了某個預料外的結果,基本上會有兩種情況:

  1. 如果沒有提供 rescue 的話,執行中止
  2. 執行 rescue 裡的程式碼

預料外的結果?

「人生不如意事十之八九,程式也會有。」

我們寫程式時總是可能會寫錯,所以噴錯是非常正常的。rubylearning.com 列出了各種可能會產生例外的情況,像是我們很熟悉的SyntaxErrorArgumentErrorRuntimeError 等等...

附上完整版供參:

這時,就是例外處理(Rescue Exception)登場救火的時機了!

先來看看怎麼寫:

begin
  # 發生了某個錯誤
rescue
  # 這個錯誤出現時要怎麼處理
ensure # (optional)
  # 無論有沒有發生例外,這一段都一定會執行
end

(Ruby 在這裡充分發揮了它的強項:程式碼非常好讀啊!)

整體運作有點像條件判斷,如果沒有出現例外,就不會進到 rescue

  • beginrescue 是正常執行時的情況
  • 一旦無法執行,就會將控制權轉移到 rescue
  • 執行 rescue 以下到 end 的區域

begin 來自於 Object 類別,基本上和end一起使用,主要是用來處理例外產生的機制,常被統稱為 begin block 或是 begin-end block


噴錯也是一種例外處理的方式

平常 Ruby 噴錯所顯示的訊息,實際上也是執行錯誤後產生的一種例外處理。直接看 code 最清楚:

begin 
  # 可能出現例外的程式碼
rescue RuntimeError
  # 出現RuntimeError時執行
  puts "RuntimeErrorr"
rescue SyntaxError
  # 出現SyntaxError時執行
  puts "SyntaxError"
rescue ArgumentError
  # 出現ArgumentError時執行
  puts "ArgumentError"
end

(這個很重要!沒有的話我就不知道噴錯的原因了!)


raise 自己做出錯誤訊息

接著再來看個例子:

def raise_exception  
  puts "準備執行 raise"
  raise "久等啦我就是錯誤訊息!"
  puts "結束執行 raise"
end
 => :raise_exception 

raise_exception

# 印出
準備執行 raise
Traceback (most recent call last):
        5: from /Users/jrnalts/.rvm/rubies/ruby-2.6.5/bin/irb:23:in `<main>'
        4: from /Users/jrnalts/.rvm/rubies/ruby-2.6.5/bin/irb:23:in `load'
        3: from /Users/jrnalts/.rvm/rubies/ruby-2.6.5/lib/ruby/gems/2.6.0/gems/irb-1.0.0/exe/irb:11:in `<top (required)>'
        2: from (irb):17
        1: from (irb):14:in `raise_exception'
RuntimeError (久等啦我就是錯誤訊息!)

從上面例子裡可以看到, "結束執行 raise" 這行並沒有被印出來,原因是我用 raise 方法觸發了例外處理,使得程式在執行時將控制權交給了 rescue

raise 是什麼?
一個來自於 Kernel 模組的方法,用途是做出一個例外實體,預設是 RuntimeError

要做出其他錯誤類型也行!

def raise_exception  
  puts "準備執行 raise"
  raise SyntaxError, "你好像寫錯什麼了雖然我不確定"
  puts "結束執行 raise"
end

raise_exception
準備執行 raise
Traceback (most recent call last):
        5: from /Users/jrnalts/.rvm/rubies/ruby-2.6.5/bin/irb:23:in `<main>'
        4: from /Users/jrnalts/.rvm/rubies/ruby-2.6.5/bin/irb:23:in `load'
        3: from /Users/jrnalts/.rvm/rubies/ruby-2.6.5/lib/ruby/gems/2.6.0/gems/irb-1.0.0/exe/irb:11:in `<top (required)>'
        2: from (irb):29
        1: from (irb):26:in `raise_exception'
SyntaxError (你好像寫錯什麼了雖然我不確定)

把所有的 rescue 都集中起來吧!

如果同時有很多地方需要 rescue,我們可以透過 rescue from 把所有例外集中處理,這樣就不用每個地方都寫 rescue

class ApplicationController < ActionController::Base
  rescue_from User::NotAuthorized, :with => :deny_access # self defined exception
  rescue_from ActiveRecord::RecordInvalid, :with => :show_errors

  rescue_from 'MyAppError::Base' do |exception|
    render :xml => exception, :status => 500
  end

  protected
    def deny_access
      ...
    end

    def show_errors(exception)
      exception.record.new_record? ? ...
    end
end

感覺方便很多呢!


例外處理的 beginend 都可以省略

如果有個例外處理方法:

def raise_exception  
  begin
    puts '咦...是不是?'  
    raise '有大事發生啦!'
    puts '哇哇哇!'
  rescue
    puts '沒事~哥已經處理好了'
  end
end  

raise_exception 

# 印出
咦...是不是?
沒事~哥已經處理好了
 => nil 

在這個凡事都有哥處理好的例子裡,如果我們省略了 beginend

def raise_exception  
  puts '咦...是不是?'  
  raise '有大事發生啦!'
  puts '哇哇哇!'
rescue
  puts '沒事~哥已經處理好了'
end  

raise_exception 

# 印出
咦...是不是?
沒事~哥已經處理好了
 => nil 

程式也是不會壞呢!真的很罩!


今天就先介紹到這邊啦!期許自己平常在 coding 時也能記得要用上 rescue 的寫法,減少程式噴錯抓錯的情況不停發生!


上一篇
陣列方法 <> 學不會就無法走出這個錯綜複雜的迷宮!路痴索隆表示:- 滿滿的紅寶石不拿嗎?
下一篇
鴨子型別 <> 終於抵達阿拉巴斯坦王國!迎接我們的是 · · · 卡魯鴨? - 滿滿的紅寶石不拿嗎?
系列文
滿滿的紅寶石不拿嗎?-- 去吧!我把世界上的一切都放在那裡了! 30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言