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 對象向標準錯誤輸出中寫入並打印每條日誌信息的日期和時間。除了 PrintlnPrintf 函數,其它的致命性函數都會在寫完日誌信息後調用 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 在某種意義上也是一種像 iffor 這樣的控制流機制。

Go 標準庫中許多地方都用了這個機制,例如,json 包中的解碼和 regexp 包中的 Complie 函數。Go 庫的原則是即使在包的內部使用了 panic,在它的對外接口(API)中也必須用 recover 處理成返回顯式的錯誤。

鏈接

results matching ""

    No results matching ""