Go 错误处理
作者:互联网
错误
在Go语言中, 错误是可以预期的,并且不是非常严重,不会影响程序的运行。对于这类问题,返回错误给调用者的方法,让调用者自行处理。
error接口
在Go语言中,错误是通过内置 error 接口实现的。它很简单,只有一个Error方法来返回具体的错误信息。
type error interface {
Error() string
}
package errors
// New returns an error that formats as the given text.
func New(text string) error {
return &errorString{text}
}
// errorString is a trivial implementation of error.
type errorString struct {
s string
}
func (e *errorString) Error() string {
return e.s
}
一般而言,error接口用于当方法或者函数执行遇到错误时进行返回,而且是第二个值。通过这种方式,可以让调用者自己根据错误信息决定如何处理。
提示:方法和函数基本上差不多,区别在于有无接收者,所以方法和函数表达的是一个意思。
error 工厂函数
我们可以自定义函数返回错误信息给调用者。通过 **errors.New()**这个工厂函数生成错误信息,它接收一个字符串参数,返回一个error接口
自定义error
工厂函数返回错误信息的方式只能传递一个字符串,如果想携带更多信息需要自定义error。自定义error其实就是先定义一个新类型,比如结构体,然后让这个类型实现error接口。
type commonError struct {
errorCode int // 错误码
errorMsg string // 错误信息
}
func (c *commonError) Error() stirng {
return c.errorMsg
}
return 0, &commonError{
errorCode : 1,
errorMSg : "不能为空",
}
error断言
有了自定义error,可以携带更多的错误信息,需要把返回的error接口转换为自定义的错误类型,需要用到类型断言。类型断言在error接口上的应用,也称之为error断言。
sum, err := add(-1, 2)
if cm, ok := sum.(*commonError); ok {
fmt.Println("错误代码为:",cm.errorCode,",错误信息为:",cm.errorMsg)
} else {
fmt.Println(sum)
}
错误嵌套
Error Wrapping
error接口虽然比较简洁,但是功能相对较弱。如果基于一个存在的error再生成一个error,这就是错误嵌套。
// 自定义结构体
type MyError struct {
err error
msg string
}
// 实现error接口,并在初始化MyError的时候传递存在的error和新的错误信息
func (e *MyError)Error() string {
return e.err.Error() + e.msg
}
func test() {
// err 是一个存在的错误
newErr := MyError(err, "错误信息")
}
上述方式可以满足需求,但是很繁琐,需要自定义新的类型还要实现error接口。所以从Go语言1.13版本开始,Go标准库新增了Error Wrapping 功能 ,让我们可以基于一个存在的error生成新的error,并且保留原error信息。
// Go语言没有提供Wrap函数,而是扩展了fmt.Errorf函数,增加一个%w,通过这种方式,可以生成wrapping error
e := errors.New("错误信息")
w := fmt.Errorf("wrap一个错误:%w", e)
errors.Unwrap函数
// error可以包括嵌套生成新error,也可以被解开,通过errors.Unwrap函数得到被嵌套的error
// Go语言提供 errors.Unwrap 用于获取被嵌套的error,获取原始错误
fmt.Println(errors.Unwrap(w))
// 输出
错误信息
errors.Is函数
由于Go语言的Error Wrapping功能,让人不知道返回的err是否被嵌套,又嵌套了几层?于是Go语言提供了error.Is 函数,用来判断两个error是否是同一个。
func Is(err, target error) bool
- 如果err和target是同一个,返回true
- 如果err是一个wrapping error,target也包含在这个嵌套error链中的话,返回true。
errors.As函数
同理,error嵌套之后,error断言也不能用了,所以Go语言为解决这个问题,提供了errors.As函数。所以我们要尽可能使用Is、As这些函数做判断和转换。
var cm *commonError
if errors.As(err, &cm) {
fmt.Println("错误代码为:",cm.errorCode,",错误信息为:",cm.errorMsg)
} else {
fmt.Println(sum)
}
Deferred函数
Go语言提供了defer函数,保证不管自定义的函数出现异常还是错误,都会被执行。defer 关键字用于修饰一个函或者方法,使得该函数或者方法在返回前才会执行,也就是被延迟,但又可以保证一定会执行。
defer语句通常被用于成对操作,如文件的打开和关闭,加锁和释放锁,连接的建立和断开等。不管多复杂的操作,都可以保证资源被正确的释放。
- 一个方法或者函数中,可以有多个defer语句
- 多个deger语句的执行顺序依照后进先出的原则
defer有一个调用栈,越早定义的越靠近栈的底部,在执行defer语句的时候,会从栈顶弹出一个defer然后执行。
Panic异常
Go语言是一门静态的强类型语言,很多问题都尽可能在编译时捕获,但是有一些只能在运行时检查,比如数组越界、不同类型强制转换,这类运行时问题会引起panic异常。除了运行时可以产生panic异常,我们也可以自己抛出panic异常。比如数据库连接。
panic是Go语言内置的函数,可以接受interface{}类型的参数,也就是任何类型的值都可以传给panic函数。
// interface{} 是空接口的意思,在Go语言中代表任意类型
func painc(v interface{})
panic异常是一种非常严重的情况,会让程序终端执行,使程序崩溃,所以如果不是影响程序运行的错误,不要使用panic,使用普通error即可。
Recover捕获Panic异常
通常情况下,我们不对panic异常做任何处理,已然已经影响到程序运行了,就直接崩溃即可。但也有些特殊情况,比如要在程序崩溃前释放资源,这时候需要从panic异常中恢复,才能完成处理。
在Go语言中,可以通过内置的Recover函数恢复panic异常。因为在程序panic异常崩溃的时候,之后被defer修饰的函数才能被执行,所以recover函数要结合defer关键字使用才能生效。
// 通过defer关键字 + 匿名函数 + recover 函数从panic异常中恢复
defer func() {
if p := recover(); p != nil {
fmt.Println(p)
}
}()
// recover函数成功捕获panic异常,返回值是通过panic函数传递的参数值
标签:errors,函数,错误信息,error,Go,错误处理,panic 来源: https://blog.csdn.net/zhw21w/article/details/121880583