1. 程式人生 > 程式設計 >go語言20小時從入門到精通(九、異常處理)

go語言20小時從入門到精通(九、異常處理)

##9.1 error介面 Go語言引入了一個關於錯誤處理的標準模式,即error介面,它是Go語言內建的介面型別,該介面的定義如下:

type error interface {
    Error() string
}
複製程式碼

Go語言的標準庫程式碼包errors為使用者提供如下方法:

package errors

type errorString struct { 
    text string 
}

func New(text string) error { 
    return &errorString{text} 
}

func (e *errorString) Error() string { 
    return
e.text } 複製程式碼

另一個可以生成error型別值的方法是呼叫fmt包中的Errorf函式:

package fmt
import "errors"

func Errorf(format string,args ...interface{}) error {
    return errors.New(Sprintf(format,args...))
}
複製程式碼

示例程式碼:

import (
    "errors"
    "fmt"
)

func main() {
    var err1 error = errors.New("a normal err1")
    fmt.Println(err1) //a normal err1

    var err2 error = fmt.Errorf("%s"
,"a normal err2")     fmt.Println(err2) //a normal err2 } 複製程式碼

函式通常在最後的返回值中返回錯誤資訊:

import (
    "errors"
    "fmt"
)

func Divide(a,b float64) (result float64,err error) {
    if b == 0 {
        result = 0.0
        err = errors.New("runtime error: divide by zero")
        return
    }

    result = a / b
    err = nil
    return
} func main() {     r,err := Divide(10.0,0)     if err != nil {         fmt.Println(err) //錯誤處理 runtime error: divide by zero     } else {         fmt.Println(r) // 使用返回值     } } 複製程式碼

##9.2 panic 在通常情況下,向程式使用方報告錯誤狀態的方式可以是返回一個額外的error型別值。

但是,當遇到不可恢復的錯誤狀態的時候,如陣列訪問越界、空指標引用等,這些執行時錯誤會引起painc異常。這時,上述錯誤處理方式顯然就不適合了。反過來講,在一般情況下,我們不應通過呼叫panic函式來報告普通的錯誤,而應該只把它作為報告致命錯誤的一種方式。當某些不應該發生的場景發生時,我們就應該呼叫panic。

一般而言,當panic異常發生時,程式會中斷執行,並立即執行在該goroutine(可以先理解成執行緒,在中被延遲的函式(defer 機制)。隨後,程式崩潰並輸出日誌資訊。日誌資訊包括panic value和函式呼叫的堆疊跟蹤資訊。

不是所有的panic異常都來自執行時,直接呼叫內建的panic函式也會引發panic異常;panic函式接受任何值作為引數。 func panic(v interface{})

呼叫panic函式引發的panic異常:

func TestA() {
    fmt.Println("func TestA()")
}

func TestB() {
    panic("func TestB(): panic")
}

func TestC() {
    fmt.Println("func TestC()")
}

func main() {
    TestA()
    TestB()//TestB()發生異常,中斷程式
    TestC()
}
複製程式碼

執行結果:

圖片.png

內建的panic函式引發的panic異常:

func TestA() {
    fmt.Println("func TestA()")
}

func TestB(x int) {
    var a [10]int
    a[x] = 222 //x值為11時,陣列越界
}

func TestC() {
    fmt.Println("func TestC()")
}

func main() {
    TestA()
    TestB(11)//TestB()發生異常,中斷程式
    TestC()
}
複製程式碼

執行結果:

圖片.png

##9.3 recover 執行時panic異常一旦被引發就會導致程式崩潰。這當然不是我們願意看到的,因為誰也不能保證程式不會發生任何執行時錯誤。

不過,Go語言為我們提供了專用於“攔截”執行時panic的內建函式——recover。它可以是當前的程式從執行時panic的狀態中恢復並重新獲得流程控制權。 func recover() interface{}

注意:recover只有在defer呼叫的函式中有效。

如果呼叫了內建函式recover,並且定義該defer語句的函式發生了panic異常,recover會使程式從panic中恢復,並返回panic value。導致panic異常的函式不會繼續執行,但能正常返回。在未發生panic時呼叫recover,recover會返回nil。

示例程式碼:

func TestA() {
    fmt.Println("func TestA()")
}

func TestB() (err error) {
    defer func() { //在發生異常時,設定恢復
        if x := recover(); x != nil {
            //panic value被附加到錯誤資訊中;
	      //並用err變數接收錯誤資訊,返回給呼叫者。
            err = fmt.Errorf("internal error: %v",x)
        }
    }()

    panic("func TestB(): panic")
}

func TestC() {
    fmt.Println("func TestC()")
}

func main() {
    TestA()
    err := TestB()
    fmt.Println(err)
    TestC()

    /*
        執行結果:
        func TestA()
        internal error: func TestB(): panic
        func TestC()
    */
}
複製程式碼

延遲呼叫中引發的錯誤,可被後續延遲呼叫捕獲,但僅最後⼀個錯誤可被捕獲:

func test() {
    defer func() {
        fmt.Println(recover())
    }()

    defer func() {
        panic("defer panic")
    }()

    panic("test panic")
}

func main() {
    test()
    //執行結果:defer panic
}
複製程式碼