Like Share Discussion Bookmark Smile

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

Go | 初步理解「恢復機制」

💬 簡介

在 Go 語言中,panicrecover 是兩個緊密相關的機制,用來處理錯誤和異常情況。當程式中出現無法繼續執行的錯誤時,panic 會中斷程式的執行,但如果與 recover 搭配使用,則可以在特定情況下恢復程式的正常運行。

本文將介紹 recover 機制的基本概念,並展示如何在程式中使用它來捕獲和處理 panic,從而達到錯誤恢復的效果。

圖片來源:Gophers


🔎 recover 基礎概念

1️⃣ 什麼是 recover

recover 是 Go 語言中的一個內建函式,主要用來從 panic 中恢復程式的正常執行。它只能在 defer 函式中使用,並且只有在 panic 被觸發後,recover 才會有效。

  • 基本的 recover 使用方法
    panic 觸發後,recover 可以捕獲 panic 的錯誤訊息,並讓程式恢復執行,而不是完全中止。

  • 範例:recover 恢復錯誤

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    package main

    import "fmt"

    func safeDivide(a, b int) int {
    defer func() {
    if r := recover(); r != nil {
    fmt.Println("恢復:", r)
    }
    }()

    if b == 0 {
    panic("除數不能為零")
    }

    return a / b
    }

    func main() {
    fmt.Println("結果:", safeDivide(10, 0))
    fmt.Println("程式繼續執行")
    }

    輸出結果:

    1
    2
    3
    恢復: 除數不能為零
    結果: 0
    程式繼續執行

    📝 在這個例子中,當 panic 被觸發後,recover 捕獲了錯誤訊息,並讓程式恢復執行,避免了程序崩潰。

2️⃣ recover 的限制

  • recover 只能在 defer 函式內部使用,這是它的使用限制之一。
  • 如果在正常的函式中使用 recover,它將無法捕獲到 panic,因為 panic 會在函式外部的執行流程中中斷。

📌 panicrecover 配合使用

recover 只能捕獲在同一個 goroutine 中觸發的 panic,並且需要 defer 搭配來確保錯誤能夠被處理和恢復。recover 會返回 panic 的值,這使得開發者可以查看和處理錯誤。


🚀 應用場景

1️⃣ 防止程式崩潰

當程式可能會遇到嚴重錯誤,像是除零錯誤或陣列越界等,使用 recover 可以有效防止程式崩潰,讓程式能夠回應錯誤並繼續運行。

  • 範例:除零錯誤的恢復
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    package main

    import "fmt"

    func divide(a, b int) (result int) {
    defer func() {
    if r := recover(); r != nil {
    fmt.Println("錯誤:", r)
    result = 0
    }
    }()

    return a / b
    }

    func main() {
    fmt.Println("結果:", divide(10, 0)) // 會觸發 panic
    fmt.Println("程式繼續執行")
    }
    輸出結果:
    1
    2
    3
    錯誤: 除數不能為零
    結果: 0
    程式繼續執行

2️⃣ 捕捉異常並處理

有時候你可能希望程式在發生錯誤時不會完全崩潰,而是根據錯誤的類型或訊息來進行相應的處理。recover 允許我們在錯誤發生後進行這樣的處理。

  • 範例:根據錯誤類型進行不同處理
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    package main

    import "fmt"

    func riskyFunction() {
    defer func() {
    if r := recover(); r != nil {
    switch r := r.(type) {
    case string:
    fmt.Println("錯誤類型是字串:", r)
    case error:
    fmt.Println("錯誤類型是錯誤:", r)
    default:
    fmt.Println("未知錯誤:", r)
    }
    }
    }()

    panic("這是一個錯誤")
    }

    func main() {
    riskyFunction()
    fmt.Println("程式繼續執行")
    }
    輸出結果:
    1
    2
    錯誤類型是字串: 這是一個錯誤
    程式繼續執行

    📝 在這個範例中,recover 不僅捕獲了錯誤,還根據錯誤的類型進行了不同的處理。

3️⃣ 用於 API 錯誤處理

在處理多個 API 請求時,若某些請求失敗,可以使用 recover 來處理這些錯誤,避免整體服務崩潰。

  • 範例:多個 API 請求的錯誤恢復
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    package main

    import "fmt"

    func apiRequest(url string) {
    defer func() {
    if r := recover(); r != nil {
    fmt.Println("API 請求錯誤,已恢復:", r)
    }
    }()

    if url == "" {
    panic("URL 為空,請求失敗")
    }

    fmt.Println("成功請求:", url)
    }

    func main() {
    apiRequest("http://example.com")
    apiRequest("") // 這會觸發 panic
    fmt.Println("其他請求繼續處理")
    }
    輸出結果:
    1
    2
    3
    成功請求: http://example.com
    API 請求錯誤,已恢復: URL 為空,請求失敗
    其他請求繼續處理

    📝 在多個 API 請求中,如果某一個請求失敗,recover 可以防止程式崩潰,繼續處理其他請求。


⚠️ 注意事項

📌 recover 的使用限制

  • recover 只有在 defer 語句內才能生效,且僅能捕獲同一個 goroutine 中發生的 panic
  • recover 不會對非 panic 錯誤(例如檢查到的錯誤或非致命錯誤)進行處理。

📌 不過度使用 recover

雖然 recover 提供了錯誤恢復的能力,但過度使用可能會導致程式流程變得難以維護。建議僅在無法預見的錯誤情況下使用,並保證錯誤能夠被合理處理。


🎯 總結

  • recover 是用來捕獲並恢復從 panic 中發生的錯誤,讓程式繼續運行。
  • recover 需要與 defer 搭配使用,並且只能捕獲當前 goroutine 中的錯誤。
  • 當程式遇到致命錯誤時,可以利用 recover 防止程式崩潰,並根據錯誤類型進行不同的處理。
  • 適當使用 recover 來增強程式的穩定性,但要避免過度依賴。

掌握 recover 機制能夠讓你的 Go 程式在面對錯誤時更加穩定和可控,讓錯誤處理變得更加精確與靈活。

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


註:以上參考了
Go