正如名字一樣,這個(recover)內(nèi)建函數(shù)被用于從 panic 或 錯誤場景中恢復:讓程序可以從 panicking 重新獲得控制權,停止終止過程進而恢復正常執(zhí)行。
recover
只能在 defer 修飾的函數(shù)(參見 6.4 節(jié))中使用:用于取得 panic 調(diào)用中傳遞過來的錯誤值,如果是正常執(zhí)行,調(diào)用 recover
會返回 nil,且沒有其它效果。
總結(jié):panic 會導致棧被展開直到 defer 修飾的 recover() 被調(diào)用或者程序中止。
下面例子中的 protect 函數(shù)調(diào)用函數(shù)參數(shù) g 來保護調(diào)用者防止從 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 包實現(xiàn)了簡單的日志功能:默認的 log 對象向標準錯誤輸出中寫入并打印每條日志信息的日期和時間。除了 Println
和 Printf
函數(shù),其它的致命性函數(shù)都會在寫完日志信息后調(diào)用 os.Exit(1),那些退出函數(shù)也是如此。而 Panic 效果的函數(shù)會在寫完日志信息后調(diào)用 panic;可以在程序必須中止或發(fā)生了臨界錯誤時使用它們,就像當 web 服務器不能啟動時那樣(參見 15.4 節(jié)中的例子)。
log 包用那些方法(methods)定義了一個 Logger 接口類型,如果你想自定義日志系統(tǒng)的話可以參考(參見 http://golang.org/pkg/log/#Logger)。
這是一個展示 panic,defer 和 recover 怎么結(jié)合使用的完整例子:
示例 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 函數(shù)。Go 庫的原則是即使在包的內(nèi)部使用了 panic,在它的對外接口(API)中也必須用 recover 處理成返回顯式的錯誤。