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

95 阅读3分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 12 天

所有语言都需要进行异常处理, Go 也一样,不过 Go 中的错误处理与其他主流编程语言(如 Java、JavaScript 或 Python)略有不同。 Go 的内置错误不包含堆栈跟踪,也不支持常规方法来处理它们。相反,Go 中的错误只是函数返回的值,并且可以像处理任何其他数据类型一样处理它们,实现非常轻量和设计也相当简单。

简介

Go 语言中的错误处理主要有三种策略:

  1. 返回和检查错误值:通过特定值表示成功和不同的错误,上层代码检查错误的值,来判断被调用函数的执行状态。
  2. 自定义错误类型:通过自定义的错误类型来表示特定的错误,上层代码通过类型断言判断错误的类型¹。
  3. 使用 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 的功能。