iT邦幫忙

2023 iThome 鐵人賽

DAY 15
0
自我挑戰組

Go in 3o系列 第 15

[Day15] Go in 30 - 錯誤處理 panic

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20230930/20162693iwTDHpSnxK.png

(圖片來源)

一、本篇提要

  • 何謂 panic
  • panic() 函式

二、何謂 panic

很多程式語言,都會用例外exception的方式來處理錯誤,例如 Java try...catch,但就如前篇我們對Go的認識,Go的錯誤會以 error 值得形式傳回,這通常不影響程式運作,但是若遇到真正嚴重的狀況,Go 還是會引發 panic。

和exception有些類似的是,panic 會在函式中一路往上傳,最終讓程式掛掉,除非我們處理它。

通常panic的引發,其實是為了要保護程式的完整性(integrity),藉由中斷程式避免上它出現其他影響,當場當掉反而是好事。

三、panic() 函式

panic 也可由開發者在程式執行期間觸發,就是呼叫panic()函式,它接受一個空介面型別 interface{} 參數,在前面篇幅有提到,空介面意味著能接受任何型別資料。

但大部分情況下,應該傳入一個error介面型別,這也是Go的慣例。

當 panic 發生時,一般會伴隨以下動作 :

  1. 停止執行。
  2. 發生panic的函式中若有延後執行(deferred)的函式,他們會被呼叫。
  3. 發生panic函式的上層函式中,若有延後執行(deffered)的函式,他們會被呼叫。
  4. 沿著函式堆疊一路往上,最後抵達main()。
  5. 發生panic的函式之後的所有敘述都不會執行。
  6. 程式當掉

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)
}

https://ithelp.ithome.com.tw/upload/images/20230930/20162693zzBxDpA5AU.png

使用 panic() 手動引發 :

package main

import (
	"errors"
	"fmt"
)

func main() {
	msg := "goodBye"
	message(msg)
	fmt.Println("這行不會印出")
}

func message(msg string) {
	if msg == "goodBye" {
		panic(errors.New("出事了"))
	}
}

預期輸出如下 :

https://ithelp.ithome.com.tw/upload/images/20230930/20162693iKx83jAzN4.png

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("出事了"))
	}
}

預期輸出 :

https://ithelp.ithome.com.tw/upload/images/20230930/20162693afLC19RAaD.png

逐步說明這段程式 :

  1. 由於 panic 發生在 message() 中,該函式內使用 defer 敘述的 fmt.Println() 會先被執行。

  2. 程式沿著函式呼叫堆疊往上,上一層就是函式test(),它當中延遲執行的 fmt.Println() 這時也執行。

  3. 接著來到 main() 函式,其中延遲執行的 fmt.Println() 也被呼叫。

  4. 最終程式因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
}

執行結果 :

https://ithelp.ithome.com.tw/upload/images/20230930/20162693CFPuOI5T1P.png

以上就是 panic 的介紹,接下來會介紹一個Go語言自有機制 recover 函式,
能在發生 panic 後用 recover() 函式將控制權還給程式~~


上一篇
[Day14] Go in 30 - 錯誤處理 -認識標準函式庫error騷兩圈
下一篇
[Day16] Go in 30 - 錯誤處理 - recover
系列文
Go in 3o30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言