其他分享
首页 > 其他分享> > Go 错误处理

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

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有一个调用栈,越早定义的越靠近栈的底部,在执行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