13.3 從 panic 中恢復(Recover)
正如名字一樣,這個(recover)內建函數被用於從 panic 或 錯誤場景中恢復:讓程序可以從 panicking 重新獲得控制權,停止終止過程進而恢復正常執行。
recover
只能在 defer 修飾的函數(參見 6.4 節)中使用:用於取得 panic 調用中傳遞過來的錯誤值,如果是正常執行,調用 recover
會返回 nil,且沒有其它效果。
總結:panic 會導致棧被展開直到 defer 修飾的 recover() 被調用或者程序中止。
下面例子中的 protect 函數調用函數參數 g 來保護調用者防止從 g 中拋出的運行時 panic,並展示 panic 中的信息:
func protect(g func()) {
defer func() {
log.Println(“done”)
// Println executes normally even if there is a panic
if err := recover(); err != nil {
log.Printf(“run time panic: %v”, err)
}
}()
log.Println(“start”)
g() // possible runtime-error
}
這跟 Java 和 .NET 這樣的語言中的 catch 塊類似。
log 包實現了簡單的日誌功能:默認的 log 對象向標準錯誤輸出中寫入並打印每條日誌信息的日期和時間。除了 Println
和 Printf
函數,其它的致命性函數都會在寫完日誌信息後調用 os.Exit(1),那些退出函數也是如此。而 Panic 效果的函數會在寫完日誌信息後調用 panic;可以在程序必須中止或發生了臨界錯誤時使用它們,就像當 web 服務器不能啓動時那樣(參見 15.4 節中的例子)。
log 包用那些方法(methods)定義了一個 Logger 接口類型,如果你想自定義日誌系統的話可以參考(參見 http://golang.org/pkg/log/#Logger)。
這是一個展示 panic,defer 和 recover 怎麼結合使用的完整例子:
示例 13.3 panic_recover.go:
// panic_recover.go
package main
import (
"fmt"
)
func badCall() {
panic("bad end")
}
func test() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("Panicing %s\r\n", e)
}
}()
badCall()
fmt.Printf("After bad call\r\n") // <-- wordt niet bereikt
}
func main() {
fmt.Printf("Calling test\r\n")
test()
fmt.Printf("Test completed\r\n")
}
輸出:
Calling test
Panicing bad end
Test completed
defer-panic-recover
在某種意義上也是一種像 if
,for
這樣的控制流機制。
Go 標準庫中許多地方都用了這個機制,例如,json 包中的解碼和 regexp 包中的 Complie 函數。Go 庫的原則是即使在包的內部使用了 panic,在它的對外接口(API)中也必須用 recover 處理成返回顯式的錯誤。
鏈接
- 目錄
- 上一節:錯運行時異常和 panic
- 下一節:自定義包中的錯誤處理和 panicking