很多程式語言,都會用例外exception的方式來處理錯誤,例如 Java try...catch,但就如前篇我們對Go的認識,Go的錯誤會以 error 值得形式傳回,這通常不影響程式運作,但是若遇到真正嚴重的狀況,Go 還是會引發 panic。
和exception有些類似的是,panic 會在函式中一路往上傳,最終讓程式掛掉,除非我們處理它。
通常panic的引發,其實是為了要保護程式的完整性(integrity),藉由中斷程式避免上它出現其他影響,當場當掉反而是好事。
panic 也可由開發者在程式執行期間觸發,就是呼叫panic()函式,它接受一個空介面型別 interface{} 參數,在前面篇幅有提到,空介面意味著能接受任何型別資料。
但大部分情況下,應該傳入一個error介面型別,這也是Go的慣例。
當 panic 發生時,一般會伴隨以下動作 :
3.1 手動引發panic
當程式有panic發生,如下 : for 迴圈遍歷索引值與nums slice 數量不符。
package main
import "fmt"
func main() {
nums := []int{2, 4, 6, 8}
total := 0
for i := 0; i <= 10; i++ {
total += nums[i]
}
fmt.Println("總和 :", total)
}
使用 panic() 手動引發 :
package main
import (
"errors"
"fmt"
)
func main() {
msg := "goodBye"
message(msg)
fmt.Println("這行不會印出")
}
func message(msg string) {
if msg == "goodBye" {
panic(errors.New("出事了"))
}
}
預期輸出如下 :
3.2 panic後,函式的defer運作 ?
在以上程式遇到panic後便不會在執行了,不過可以來看看,如果 panic 後面的函式加上 defer 敘述的執行效果 :
package main
import (
"fmt"
"errors"
)
func main() {
defer fmt.Println("在main()使用defer")
test()
fmt.Println("這一行不會印出")
}
func test() {
defer fmt.Println("在test()使用defer")
msg := "good-bye"
message(msg)
}
func message(msg string) {
defer fmt.Println("在 message() 使用 defer")
if msg == "good-bye" {
panic(errors.New("出事了"))
}
}
預期輸出 :
逐步說明這段程式 :
由於 panic 發生在 message() 中,該函式內使用 defer 敘述的 fmt.Println() 會先被執行。
程式沿著函式呼叫堆疊往上,上一層就是函式test(),它當中延遲執行的 fmt.Println() 這時也執行。
接著來到 main() 函式,其中延遲執行的 fmt.Println() 也被呼叫。
最終程式因panic()中斷
Note : Go中也有其他可停止程式的做法,os.Exit(1) 會立即中斷程式,並傳會一個狀態碼,(習慣上0為正常,整數代表錯誤,其意義由使用者決定,但使用 os.Exit() 時,一律不會執行被延遲的函式,因此在特定情況下,使用 panic() 會比使用os.Exit()來的合適
接下來練習利用 panic ,讓程式中斷 :
範例 :
package main
import "fmt"
func main() {
fmt.Println("程式開始執行")
// 模擬發生錯誤
err := simulateError()
if err != nil {
fmt.Println("發生錯誤:", err)
}
fmt.Println("程式繼續執行")
}
func simulateError() error {
fmt.Println("模擬錯誤發生")
panic("發生致命錯誤") // 使用 panic() 函數觸發錯誤
// 這裡的程式碼不會被執行
fmt.Println("這段程式碼不會被執行")
return nil
}
執行結果 :
以上就是 panic 的介紹,接下來會介紹一個Go語言自有機制 recover 函式,
能在發生 panic 後用 recover() 函式將控制權還給程式~~