Go | 初步理解「恢復機制」
💬 簡介
在 Go 語言中,panic
和 recover
是兩個緊密相關的機制,用來處理錯誤和異常情況。當程式中出現無法繼續執行的錯誤時,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
22package 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
會在函式外部的執行流程中中斷。
📌 panic
與 recover
配合使用
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
19package 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
25package 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
23package 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