Go语言的错误机制是基于返回值的方式,它使用了一个特殊的内置类型 error 来表示错误,并且鼓励开发者以通用的方式处理错误。
1 error 类型
error 是一个内置的接口类型,通常由函数返回来表示执行过程中的错误状态。它只有一个方法,即 Error() 方法,用于返回错误的描述信息。
type error interface {
Error() string
}
1.1 自定义 error
创建一个自定义错误类型的结构体,通常以该类型的名字作为结构体名。该结构体需要满足 error 接口,即需要实现 Error() string 方法。
// 自定义错误类型
type MyError struct {
message string
}
// 实现 error 接口的 Error 方法
func (e MyError) Error() string {
return e.message
}
1.2 使用 error
函数可以通过返回 error 来指示它们的执行是否成功。如果函数执行成功,通常返回 nil;如果发生错误,就返回一个非 nil 的错误。
func function() error {
if someCondition {
return nil
} else {
return MyError{"Something went wrong"}
}
}
errors 包是一个标准库中的包,用于创建和处理错误。它提供了一个用于创建简单错误值的函数,以及用于检查错误类型的函数。
-
errors包提供了一个函数New,用于创建一个新的错误值。这对于创建简单的错误信息非常有用。import "errors" func function() error { return errors.New("Something went wrong") } -
可以使用
errors包中的函数来检查一个错误是否是特定类型的错误。errors.Is(err, target)用于检查一个错误是否与目标错误类型匹配。这个函数返回一个布尔值,表示给定的错误是否是目标错误类型的实例或包装。import "errors" err := function() if err != nil { if errors.Is(err, io.EOF) { // 处理 EOF 错误 } } -
Go语言支持错误链,这意味着你可以将一个错误包装在另一个错误中,以保留更多的上下文信息。
fmt.Errorf是 Go 语言标准库fmt包中提供的一个函数,用于格式化生成一个新的错误。它类似于使用字符串格式化的方式创建错误,并且可以通过%w占位符将一个错误包装在另一个错误中,以提供更多的上下文信息。if err := function(); err != nil { return fmt.Errorf("encountered an error: %w", err) } -
通过使用错误的
Error()方法,你可以获取错误的描述信息。这对于日志记录和用户友好的错误显示非常有用。import "fmt" err := function() if err != nil { fmt.Println("Error:", err) }将一个实现了
error接口的对象传递给fmt.Println函数或类似的函数时,它们会自动调用对象的Error()方法来获取错误的描述信息,并将其打印出来。
2 panic 和 recover
2.1 defer
defer 是一个关键字,用于延迟(defer)一个函数的执行,直到包含它的函数执行完毕。这意味着无论函数是正常返回还是发生异常退出,被延迟的函数都会在函数退出之前被调用。defer 常用于执行一些清理操作,如关闭文件、释放资源等,以确保在函数结束时这些操作都会被执行。
defer function(arguments)
- 如果一个函数内有多个
defer,它们会按照倒序的顺序执行,即最后一个defer会最先执行,依此类推。 defer延迟的函数在当前函数执行完毕之后才会执行,即使在函数中有return语句,也会等待defer的函数执行完毕之后再返回。
2.2 panic + recover
panic 和 recover 是用于处理程序中的异常情况的机制。它们通常用于处理无法恢复的错误和异常。
panic 是一个内置函数,用于表示程序中出现了无法继续执行的严重错误。当程序调用 panic 函数时,当前函数的执行会立即停止,程序会从当前函数的调用栈中退出,然后开始执行每层调用栈的 defer 函数。如果没有遇到 recover,程序将终止并显示一个运行时错误信息。
panic 可以是开发者主动触发的,也可以是某些不可控的错误导致的(如数组越界、空指针解引用等)。
func main() {
panic("Something went wrong")
}
recover 是一个内置函数,用于在 defer 函数中捕获 panic 导致的程序中断,从而允许程序进行恢复操作。recover 必须在 defer 内部调用,否则它将不会起作用。当 recover 被调用时,它会返回 panic 传递的值,如果没有发生 panic,它将返回 nil。
通常,recover 被用于恢复程序的控制流,以便进行一些清理操作或者继续执行一部分逻辑。
func main() {
// 触发 panic,这里会引发一个中断,但由于下面的 defer 函数,程序不会终止
panic("Something went wrong")
// 使用 defer 定义一个匿名函数,在 main 函数结束时执行该函数
defer func() {
// 在这个匿名函数内部使用 recover 来捕获 panic
if r := recover(); r != nil {
fmt.Println("Recovered:", r)
}
}()
}
3 错误处理用例
在Go语言中,编写返回两个值的函数并且其中一个值是 error 类型是非常常见的。这种模式通常用于表示函数的执行结果是否成功,以及在出现错误时提供错误信息。
package main
import (
"errors"
"fmt"
)
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
func main() {
result, err = divide(10, 0)
if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println("Result:", result)
}
}
4 注意事项
- 不要仅仅使用
if err != nil进行错误检查。错误可能是其他一些值,需要根据情况进行特定的错误处理。 - 不要简单地使用
_或忽略错误,因为这可能导致潜在的问题被忽略,从而影响程序的健壮性。 - 除非是无法从中恢复的情况,避免使用
panic。使用panic可能会导致程序不可控制地终止,而不是优雅地处理错误。