##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
}
复制代码