Go语言中的错误处理最佳实践详解
作者:Alan_w
错误处理实践
我们在go语言中设计error的处理体系时候, 一般都会去做下面两点
- 直接使用errors.New()生成error接口的值
- 扩展error接口, 并定义扩展error接口的实现类型
error接口是什么
go语言的error是一个接口类型, 其源码如下:
type error interface { Error() string }
我们可以定义它的实现类型, 比如我们经常使用到的errors.New()
方法, 返回值为一个error接口的实现类型*errorString
的结构体字面量
package errors func New(text string) error { return &errorString{text} } type errorString struct { s string } func (e *errorString) Error() string { return e.s } //package main xxxerr := errors.New("xxx")
所以我们可以直接调用errors.New()
为我们生成一个error接口的值
扩展的error接口
我们为什么需要对error接口进行扩展呢?, 原因是error的实现类型范围太大了, 细粒度不够小, 所以我们需要实现更加精细的控制, 关于这种设计我们可以参考go语言标准库中的一些error处理代码, 比如下面的net.Error
type Error interface { //嵌入了error接口, 实现net.Error也会实现error error //扩展 Timeout() bool Temporary() bool }
然后我们又可以定义一个类型来实现这个扩展错误接口类型, 比如下面这个OpError:
type OpError struct { Op string Net string Source Addr Addr Addr Err error } //实现函数1 func (e *OpError) Error() string { return "" } //下面是实现函数2 func (e *OpError) Timeout() bool { //对应的处理逻辑 return true } //下面是实现函数2 func (e *OpError) Temporary() bool {}
我们发现该结构体中存在一个名字叫做Err的类型为error的字段, 它代表了该错误的潜在错误, 有可能OpError类型的错误值还包含了AddrError这种错误
通过这种类型建立起树形的错误体系, 用统一字段建立可追溯的链式错误关联, 我们就可以建立起来一套优秀的错误处理机制
为了更好的表示, 我画了一张图
具体的错误
因为Go语言的error是一个接口, 所以这个它的值的实际类型是非常复杂的, 于是我们就需要去判断它的值的一个实际类型
- 如果错误值在某一个范围内, 我们可以使用类型断言表达式或者类型断言+switch语句进行判断
- 对于已有相应变量且类型相同的一系列错误值, 一般直接使用判等操作 + switch语句
- 没有相应变量且类型未知的一系列错误值, 只能使用其错误信息的字符串表示形式来判断
下面我们分别来看上面的内容: 首先是第一点, 已知错误值的范围比如: {os.PathError|os.LinkError|os.SyscallError|exec.Error}
, 是它们中的一个, 我们可以直接使用类型断言+switch, 然后返回潜在错误类型
func underlyingError(err error) error { switch err := err.(type) { case *os.PathError: return err.Err case *os.LinkError: return err.Err case *os.SyscallError: return err.Err case *exec.Error: return err.Err } return err }
当我已经知道某个错误是哪一个, 我们直接使用判等操作+switch,
printError := func(i int, err error) { if err == nil { fmt.println("nil error") return } err = underlyingError(err) switch err { case os.ErrClosed: fmt.Printf("error(closed)[%d]: %s\n", i, err) case os.ErrInvalid: fmt.Printf("error(invalid)[%d]: %s\n", i, err) case os.ErrPermission: fmt.Printf("error(permission)[%d]: %s\n", i, err) } }
通过上面这种直接判等操作, 我们就可以锁定具体的错误值了
对于上面两种情况, 我们都会有比较明确的方法去解决, 但是我们对一个错误值可能代表的含义知道的很少, 那么就只能通过错误信息去判断了
到此这篇关于Go语言中的错误处理最佳实践详解的文章就介绍到这了,更多相关Go错误处理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!