ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Go 错误处理

2021-12-12 10:03:59  阅读:225  来源: 互联网

标签:errors 函数 错误信息 error Go 错误处理 panic


错误

​ 在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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有