Go Wiki: PanicAndRecover

目錄

Panic

panicrecover 函式在某些其他語言中類似於異常的 try/catch,因為 panic 會導致程式堆疊開始展開,而 recover 可以阻止它。延遲的函式在堆疊展開時仍會被執行。如果在這種延遲函式中呼叫 recover,堆疊展開將停止,並且 recover 會返回傳遞給 panic 的值(作為 interface{})。在非正常情況下,例如陣列或切片越界索引,執行時也會 panic。如果 panic 導致堆疊展開到任何正在執行的 goroutine 之外(例如,main 或傳遞給 go 的頂層函式未能從中恢復),程式將退出,並附帶所有正在執行的 goroutines 的堆疊跟蹤。一個 goroutine 中的 panic 不能被另一個 goroutine recover

包中的用法

根據慣例,不應允許顯式的 panic() 跨越包邊界。應透過返回錯誤值來向呼叫者指示錯誤情況。然而,在包內部,尤其是在對非匯出函式進行深度巢狀呼叫時,使用 panic 來指示應該轉換為錯誤並返回給呼叫函式的錯誤情況可能非常有用(並提高可讀性)。下面是一個不算巧妙的例子,說明了一個巢狀函式和一個匯出函式可能透過這種基於 panic 的錯誤關係進行互動。

// A ParseError indicates an error in converting a word into an integer.
type ParseError struct {
    Index int    // The index into the space-separated list of words.
    Word  string // The word that generated the parse error.
    Error error  // The raw error that precipitated this error, if any.
}

// String returns a human-readable error message.
func (e *ParseError) String() string {
    return fmt.Sprintf("pkg: error parsing %q as int", e.Word)
}

// Parse parses the space-separated words in input as integers.
func Parse(input string) (numbers []int, err error) {
    defer func() {
        if r := recover(); r != nil {
            var ok bool
            err, ok = r.(error)
            if !ok {
                err = fmt.Errorf("pkg: %v", r)
            }
        }
    }()

    fields := strings.Fields(input)
    numbers = fields2numbers(fields)
    return
}

func fields2numbers(fields []string) (numbers []int) {
    if len(fields) == 0 {
        panic("no words to parse")
    }
    for idx, field := range fields {
        num, err := strconv.Atoi(field)
        if err != nil {
            panic(&ParseError{idx, field, err})
        }
        numbers = append(numbers, num)
    }
    return
}

為了演示這種行為,請考慮以下 main 函式

func main() {
    var examples = []string{
        "1 2 3 4 5",
        "100 50 25 12.5 6.25",
        "2 + 2 = 4",
        "1st class",
        "",
    }

    for _, ex := range examples {
        fmt.Printf("Parsing %q:\n  ", ex)
        nums, err := Parse(ex)
        if err != nil {
            fmt.Println(err)
            continue
        }
        fmt.Println(nums)
    }
}

參考

延遲、Panic 和 Recover

https://golang.com.tw/ref/spec#Handling_panics

https://golang.com.tw/ref/spec#Run_time_panics


此內容是 Go Wiki 的一部分。