Like Share Discussion Bookmark Smile

J.J. Huang   2025-03-06   Getting Started Golang 04.函式   瀏覽次數:次   DMCA.com Protection Status

Go | 初步理解「恐慌機制」

💬 簡介

在 Go 語言中,panic 是一種用來處理程式異常狀況的機制。當程式出現無法繼續執行的錯誤時,panic 會觸發程序的異常終止,並使程式進入錯誤處理流程。理解 panic 及其應用對於提升程式的錯誤處理能力非常重要。

本文將介紹 panic 的基本概念,並探討如何使用 defer 搭配 panic,有效控制程式中錯誤處理的流程。

圖片來源:Gophers


🔎 panic 基礎概念

1️⃣ 什麼是 panic

panic 是 Go 語言中的一個內建函式,當程式執行到某個錯誤狀況時,使用 panic 會立刻中斷當前程式的執行,並進入錯誤處理流程。

  • 範例:觸發 panic
1
2
3
4
5
6
7
8
9
package main

import "fmt"

func main() {
fmt.Println("程式開始")
panic("程式發生錯誤,立刻中止!")
fmt.Println("這行程式碼不會執行")
}

輸出結果:

1
2
3
4
5
6
7
程式開始
panic: 程式發生錯誤,立刻中止!

goroutine 1 [running]:
main.main()
D:/workspace/example/main.go:6 +0x60
exit status 2

📝 當 panic 被觸發時,程式會立即中止並印出錯誤訊息。

📌 panic 的應用情境

panic 通常用來處理程式中無法繼續執行的錯誤,像是:

  • 無效的輸入或資料
  • 系統資源錯誤
  • 程式的邏輯錯誤等

當發現錯誤無法繼續執行時,panic 使得程式能夠儘早停止,避免錯誤影響到後續處理。


🛠 panicdefer 搭配使用

1️⃣ defer 確保資源釋放

defer 會在函式返回時延遲執行,它不僅用來釋放資源,也可以用來處理 panic 觸發時的錯誤恢復。當 panic 發生時,defer 的語句仍然會被執行,這樣可以確保即使程式中止,資源也會得到正確釋放。

  • 範例:panic 與 defer 結合使用
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    package main

    import "fmt"

    func main() {
    defer fmt.Println("程式結束")
    fmt.Println("程式開始")

    panic("發生錯誤,程式無法繼續執行")
    fmt.Println("這行程式碼不會執行")
    }
    輸出結果:
    1
    2
    3
    4
    程式開始
    panic: 發生錯誤,程式無法繼續執行

    程式結束

    📝 即使 panic 被觸發,defer 的語句仍然會在函式返回時執行,確保資源或其他必要的操作能夠完成。

2️⃣ defer 的執行順序

panic 被觸發後,Go 語言會進入錯誤處理流程,並按照「後進先出」(LIFO) 的順序執行所有 defer 語句。

  • 範例:多個 defer 和 panic 的順序
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    package main

    import "fmt"

    func main() {
    defer fmt.Println("第一個延遲執行")
    defer fmt.Println("第二個延遲執行")
    fmt.Println("程式開始")

    panic("發生錯誤,程式中止!")
    fmt.Println("這行程式碼不會執行")
    }
    輸出結果:
    1
    2
    3
    4
    5
    程式開始
    panic: 發生錯誤,程式中止!

    第二個延遲執行
    第一個延遲執行

    📝 當 panic 發生時,defer 語句會按照後進先出(LIFO)的順序執行,即最後被 defer 的會最先執行。


🚀 應用場景

1️⃣ 必須設置環境變數

在一些情況下,程式可能需要依賴特定的環境變數來運行。例如,如果程式需要與資料庫連線,卻發現必要的環境變數(例如資料庫主機地址)未設置時,可以使用 panic 來中止程式的執行。

  • 範例:資料庫位置未設定
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    package main

    import (
    "fmt"
    "os"
    )

    func main() {
    dbHost := os.Getenv("DB_HOST")
    if dbHost == "" {
    panic("資料庫主機環境變數未設定,程式無法繼續執行!")
    }

    fmt.Println("資料庫位置:", dbHost)
    }
    輸出結果(如果環境變數未設定):
    1
    panic: 資料庫主機環境變數未設定,程式無法繼續執行!

    📝 當環境變數未正確設定,程式無法繼續執行時,可以使用 panic 強制終止程式,避免錯誤地繼續執行。

2️⃣ 配置檔案缺失或格式錯誤

如果程式需要從某個配置檔案讀取設定,當檔案缺失或格式錯誤時,程式可能無法正常啟動。在這種情況下,也可以使用 panic 來強制中止程式的執行。

  • *範例:配置檔案缺失
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    package main

    import (
    "fmt"
    "os"
    )

    func main() {
    configFile := "config.yaml"
    if _, err := os.Stat(configFile); os.IsNotExist(err) {
    panic("配置檔案缺失,程式無法啟動!")
    }

    fmt.Println("配置檔案存在:", configFile)
    }
    輸出結果(如果配置檔案缺失):
    1
    panic: 配置檔案缺失,程式無法啟動!

    📝 當某些必須的檔案不存在或格式錯誤時,可以使用 panic 停止程式,避免後續錯誤。

3️⃣ 重要功能或服務無法啟動

某些系統依賴於特定的外部服務(如 API 服務或外部資源)。如果這些服務無法啟動,程式不應該繼續執行,這時可以使用 panic 來強制中止。

  • 範例:外部 API 服務不可用
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    package main

    import "fmt"

    func main() {
    serviceStatus := false
    if !serviceStatus {
    panic("外部 API 服務不可用,程式無法繼續執行!")
    }

    fmt.Println("成功連接到外部 API")
    }
    輸出結果:
    1
    panic: 外部 API 服務不可用,程式無法繼續執行!

    📝 當程式無法依賴關鍵服務時,可以使用 panic 來中止,避免錯誤執行。


⚠️ 注意事項

📌 何時應該使用 panic

panic 主要用於當程式面臨不可預期的錯誤且無法繼續執行時。對於一些較為常見或可處理的錯誤,應該使用錯誤處理機制(例如 error 類型)來避免不必要的程式中止。

📌 panic 的性能開銷

panicdefer 會對程式的執行效率產生一些影響,尤其是在錯誤頻繁發生的情況下。因此,應該謹慎使用,避免在正常流程中使用 panic

📌 程式碼結構

當使用 panic 時,程式的控制流程會被中斷,並且堆疊追蹤會顯示 panic 發生的位置。這對於錯誤追蹤非常有用,但同時也會影響程式的效率,因此應當在必要的情況下使用。


🎯 總結

  • panic 用於程式中發生無法恢復的錯誤時,立即中止程式的執行。
  • defer 可以與 panic 搭配使用,確保即使程式中止,資源仍然能夠正確釋放。
  • 多個 defer 語句會按照後進先出(LIFO)的順序執行。
  • 使用 panic 時要謹慎,並且確保只在無法恢復的錯誤情況下使用。

掌握 panicdefer 的配合使用,能夠讓你的程式在面對錯誤時更穩定、可控。

最後建議回顧一下 Go | 菜鳥教學 目錄,了解其章節內容。


註:以上參考了
Go