Go语言中的错误和异常处理 | 青训营笔记

100 阅读2分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 5 天,记录一下对 go 语言错误和异常处理的学习。

简单错误

简单错误是指仅出现一次的错误,且在其他地方不需要捕获该错误。

简单错误优先使用 errors.New 来创建,如果需要格式化,则可以使用 fmt.Errorf 来创建;用这两个函数创建的错误都是不同的,即使传入的字符串相同。

简单错误应当写清楚出错的原因。

下面的代码中定义了一个 check 函数,该函数可能会返回简单错误,描述了出错的原因。

 func check(n int) error {
     if n > 10 {
         // return errors.New("n is bigger than 10")
         return fmt.Errorf("%d is bigger than 10", n)
     }
     return nil
 }
错误链

Go 1.13 在 errors 中新增了三个 api 和 一个新的 format 关键字,分别是 errors.Is、errors.As、errors.Unwrap 和 fmt.Errorf 的 %w。

在 fmt.Errorf 中使用 %w 关键字可以将一个错误关联至错误链中,即实现了错误的 wrap;使用 errors.Unwrap 函数可以打开一个被包装的错误,获得 %w 代表的错误(如果没有则返回 nil),即实现了错误的 unwrap。

要判断错误链上是否包含特定错误,需要使用 errors.Is 函数,不能使用 ==。

要在错误链上获取特定种类的错误,需要使用 errors.As 函数。

panic 和 recover

Panic 比错误更严重,它的出现表示程序无法正常工作了。在 panic 发生后,会向上传播至调用栈顶,如果当前 goroutine 中所有的 defer 函数都不包含 recover 就会造成整个程序崩溃。

因此,不建议在业务代码中使用 panic,但当程序启动阶段发生严重错误时(如数据库连接失败等),可以在 init 或 main 函数中使用 painc。

Recover 只能在 defer 函数中使用,且只在当前 goroutine 生效。多个 defer 会形成 defer 栈,后定义的 defer 语句会被最先调用。

可以在 recover 之后记录堆栈调用的信息,方便定位问题。

 defer func() {
     if e := recover(); e != any(nil) {
         ...
         log.Println(string(debug.Stack()))
     }
 }()