即使程式碼本身正確無誤,我們仍然要面對程式運行時可能發生的錯誤,像是網路無法連線、檔案或資料夾權限不足、檔案格式錯誤、命令列參數錯誤、除以零等運算錯誤等。在實務上,我們不能一廂情願地認定程式不會發生錯誤,而要撰寫相對應的程式碼來處理錯誤。
在程式語言中,錯誤處理 (exception handling) 分為兩類,像 C 等比較老派的語言沒有內建錯誤處理的語法,通常就是直接使用 if
等控制流程來控制錯誤發生時程式運作的過程;現代語言大部分都會使用 try ... catch ...
或同義的語法來處理錯誤。基本上,錯誤處理算是另一種控制流程,只是這套流程僅用於錯誤或例外發生時。本文介紹 Nim 的例外處理。
在 Nim 語言中,將例外 (Exception) 包裝成物件,可到這裡觀看其例外物件的階層。引發例外的方式是用 raise
呼叫例外物件,如以下短例:
raise newException(Exception, "Something wrong")
執行此程式會得到以下結果:
$ ./raise
raise.nim(1) raise
Error: unhandled exception: Something wrong [Exception]
當例外物件引發時,若沒有進一步處理該物件,預設情形下會導致程式從例外物件所在處中止程式。有時候這樣的處理方式不是我們想要的,Nim 提供 try ... except ... finally
區塊,讓程式設計者可以決定錯誤發生時程式如何運作。如以下短例:
proc doSomething() =
raise newException(Exception, "Something wrong")
try:
doSomething()
except:
echo "The program ended unexpectedly"
執行此程式會得到以下結果:
$ ./try
The program ended unexpectedly
比較這兩個例子可以看出,例外發生時是否有人為介入會有不同的效果。如果撰寫函式庫,使用的對象是程式設計者,必要時直接拋出例外倒無可厚非,但在撰寫應用程式時,直接拋出例外會讓使用者以為程式發生什麼臭蟲,一些早期的 Java 程式沒有考慮周詳可能的例外時,就會噴出一長串讓人難以理解的例外訊息。對於應用程式來說,將例外進行進一步處理,而不是突然結束,對使用者來說較為友善。
對於外部資源輸出入,可使用 defer
語法,讓程式碼看起來更簡潔,這是向 Go 語言致敬的語法。實例如下:
var f = open("numbers.txt")
defer: close(f)
f.write "abc"
f.write "def"
上例和以下的範例同義:
var f = open("numbers.txt")
try:
f.write "abc"
f.write "def"
finally:
close(f)
讀者可從中自行選擇喜歡的語法。