一 error接口
Go语言引入了一个关于错误处理的标准模式,即error
接口,它是Go语言内建的接口类型,该接口的定义如下:
type error interface { |
Go语言的标准库代码包errors
为用户提供如下方法:
package errors |
我们可以在编码中通过实现 error
接口类型来生成错误信息。
函数通常在最后的返回值中返回错误信息。使用errors.New
可返回一个错误信息:
func Sqrt(f float64) (float64, error) { |
另一个可以生成error
类型值的方法是调用fmt
包中的Errorf
函数:
package fmt |
示例代码:
import ( |
函数通常在最后的返回值中返回错误信息:
import ( |
二 panic
在通常情况下,向程序使用方报告错误状态的方式可以是返回一个额外的error
类型值。
但是,当遇到不可恢复的错误状态的时候,如数组访问越界、空指针引用等,这些运行时错误会引起painc
异常。这时,上述错误处理方式显然就不适合了。反过来讲,在一般情况下,我们不应通过调用panic
函数来报告普通的错误,而应该只把它作为报告致命错误的一种方式。当某些不应该发生的场景发生时,我们就应该调用panic
。
一般而言,当panic异常发生时,程序会中断运行,并立即执行在该goroutine
(可以先理解成线程,在中被延迟的函数(defer
机制)。随后,程序崩溃并输出日志信息。日志信息包括panic value
和函数调用的堆栈跟踪信息。
不是所有的panic
异常都来自运行时,直接调用内置的panic
函数也会引发panic
异常;panic
函数接受任何值作为参数。
func panic(v interface{}) |
调用panic函数引发的panic异常:package main
import (
"fmt"
)
func TestA() {
fmt.Println("func TestA()")
}
func TestB() {
panic("func TestB(): panic")
}
func TestC() {
fmt.Println("func TestC()")
}
func main() {
TestA()
TestB() //TestB()发生异常,中断程序
TestC()
}
运行结果:
C:/Users/qingteng/Desktop/aa/src/src.exe [C:/Users/qingteng/Desktop/aa/src] |
内置的panic函数引发的panic异常:
package main |
运行结果:
C:/Go/bin/go.exe build [C:/Users/qingteng/Desktop/aa/src] |
三 recover
运行时panic
异常一旦被引发就会导致程序崩溃。这当然不是我们愿意看到的,因为谁也不能保证程序不会发生任何运行时错误。
不过,Go语言为我们提供了专用于“拦截”运行时panic
的内建函数——recover
。它可以是当前的程序从运行时panic
的状态中恢复并重新获得流程控制权。
func recover() interface{} |
注意:recover
只有在defer
调用的函数中有效。
3.1 调用内置函数recover
如果调用了内置函数recover
,并且定义该defer
语句的函数发生了panic
异常,recover
会使程序从panic
中恢复,并返回panic value
。导致panic
异常的函数不会继续运行,但能正常返回。在未发生panic
时调用recover
,recover
会返回nil
。
例代码:
package main |
运行结果为
C:/Go/bin/go.exe build [C:/Users/qingteng/Desktop/aa/src] |
3.2 延迟调用中引发的错误
延迟调用中引发的错误,可被后续延迟调用捕获,但仅最后⼀个错误可被捕获:
package main |
运行结果为
C:/Users/qingteng/Desktop/aa/src/src.exe [C:/Users/qingteng/Desktop/aa/src] |
四 panic 与 recover区别
这里应该介绍一下 panic
与 recover
,一个用于主动抛出错误,一个用于捕获panic
抛出的错误。
概念
panic
与 recover
是 Go 的两个内置函数,这两个内置函数用于处理 Go 运行时的错误,panic
用于主动抛出错误,recover
用来捕获 panic
抛出的错误。
- 引发
panic
有两种情况,一是程序主动调用,二是程序产生运行时错误,由运行时检测并退出。 - 发生
panic
后,程序会从调用panic
的函数位置或发生panic
的地方立即返回,逐层向上执行函数的defer
语句,然后逐层打印函数调用堆栈,直到被recover
捕获或运行到最外层函数。 panic
不但可以在函数正常流程中抛出,在defer
逻辑里也可以再次调用panic
或抛出panic
。defer
里面的panic
能够被后续执行的defer
捕获。recover
用来捕获panic
,阻止panic
继续向上传递。recover()
和defer
一起使用,但是defer
只有在后面的函数体内直接被掉用才能捕获panic
来终止异常,否则返回nil
,异常继续向外传递。
例子1
//以下捕获失败 |
例子2
多个panic只会捕捉最后一个:
package main |
使用场景
一般情况下有两种情况用到:
- 程序遇到无法执行下去的错误时,抛出错误,主动结束运行。
- 在调试程序时,通过 panic 来打印堆栈,方便定位错误。