这是我参与「第五届青训营 」伴学笔记创作活动的第 12 天
所有语言都需要进行异常处理, Go 也一样,不过 Go 中的错误处理与其他主流编程语言(如 Java、JavaScript 或 Python)略有不同。 Go 的内置错误不包含堆栈跟踪,也不支持常规方法来处理它们。相反,Go 中的错误只是函数返回的值,并且可以像处理任何其他数据类型一样处理它们,实现非常轻量和设计也相当简单。
简介
Go 语言中的错误处理主要有三种策略:
- 返回和检查错误值:通过特定值表示成功和不同的错误,上层代码检查错误的值,来判断被调用函数的执行状态。
- 自定义错误类型:通过自定义的错误类型来表示特定的错误,上层代码通过类型断言判断错误的类型¹。
- 使用 defer, panic, recover 机制:Go 中可以抛出一个 panic 的异常,然后在 defer 中通过 recover 捕获这个异常,然后正常处理
下面我将分别介绍这三种策略。
返回和检查错误值
返回和检查错误值是 Go 语言中最常用的异常处理策略。它的基本思想是,函数可以返回一个 error 类型的值,表示成功或不同的错误情况。上层代码需要检查 error 的值,来判断函数是否执行正常¹。
例如,下面的代码使用了 os.Open 函数来打开一个文件,并检查了返回的错误值:
f, err := os.Open("filename.ext")
if err != nil {
log.Fatal(err)
}
// do something with f
这种策略的几个优点是:
- 简单明了,符合 Go 语言的设计哲学。
- 强制调用者对错误进行处理,避免忽略错误导致潜在的问题。
- 可以自定义 error 的内容和格式,提供更多的信息和上下文。
自定义错误类型
自定义错误类型是 Go 语言中的另一种异常处理策略。它的基本思想是,通过自定义的数据类型来表示特定的错误,上层代码通过类型断言判断错误的类型。
例如,下面的代码使用了一个自定义的 MyError 类型来表示一个错误:
type MyError struct {
Msg string
File string
Line int
}
func (e *MyError) Error() string {
return fmt.Sprintf("%s:%d: %s", e.File, e.Line, e.Msg)
}
func test() error {
return &MyError{"Something happened", "server.go", 42}
}
这种策略的几个优点是:
- 可以提供更多的错误信息和上下文,方便调试和定位问题。
- 可以根据不同的错误类型进行不同的处理逻辑,增加灵活性和可扩展性。
- 可以利用接口和组合等特性,实现更复杂的错误结构和行为。
使用 defer, panic, recover 机制
使用 defer, panic, recover 机制是 Go 语言中的第三种异常处理策略。它的基本思想是,通过 panic 函数来抛出一个异常,通过 defer 函数来延迟执行一些清理或恢复的操作,通过 recover 函数来捕获并处理 panic 引发的异常¹。
例如,下面的代码使用了 defer, panic, recover 机制来处理一个除以零的错误:
func div(a, b int) {
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
}
}()
fmt.Println(a / b)
}
func main() {
div(10, 0)
}
这种策略的几个优点是:
- 可以在不影响正常逻辑的情况下,处理一些不可预知或不可恢复的错误。
- 可以在任何地方引发 panic ,但只能在 defer 调用的函数中使用 recover ,这样可以避免滥用异常机制。
- 可以利用 defer 的特性,实现一些类似于 try-finally 的功能。