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

##9.1 error接口 Go語言引入了一個關於錯誤處理的標準模式,即error接口,它是Go語言內建的接口類型,該接口的定義以下:數組

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

Go語言的標準庫代碼包errors爲用戶提供以下方法:bash

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函數:ide

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
}
複製代碼

函數一般在最後的返回值中返回錯誤信息:ui

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類型值。spa

可是,當遇到不可恢復的錯誤狀態的時候,如數組訪問越界、空指針引用等,這些運行時錯誤會引發painc異常。這時,上述錯誤處理方式顯然就不適合了。反過來說,在通常狀況下,咱們不該經過調用panic函數來報告普通的錯誤,而應該只把它做爲報告致命錯誤的一種方式。當某些不該該發生的場景發生時,咱們就應該調用panic。線程

通常而言,當panic異常發生時,程序會中斷運行,並當即執行在該goroutine(能夠先理解成線程,在中被延遲的函數(defer 機制)。隨後,程序崩潰並輸出日誌信息。日誌信息包括panic value和函數調用的堆棧跟蹤信息。指針

不是全部的panic異常都來自運行時,直接調用內置的panic函數也會引起panic異常;panic函數接受任何值做爲參數。 func panic(v interface{})日誌

調用panic函數引起的panic異常:code

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
}
複製代碼
相關文章
相關標籤/搜索