Golang语言将错误作为值来处理,标准库中的errors包是创建和处理错误的基础工具,掌握它的使用方法能让错误处理逻辑更加清晰规范。在实际开发中,我们可以根据场景选择不同的错误创建方式,满足从简单提示到复杂错误链传递的需求。

基础错误创建:errors.New函数
errors包最核心的函数是errors.New,它可以创建一个包含指定错误信息的简单错误实例,适用于不需要额外上下文的简单错误场景。
下面是基础使用的示例:
package main
import (
"errors"
"fmt"
)
func divide(a, b int) (int, error) {
// 除数为0时创建错误
if b == 0 {
return 0, errors.New("除数不能为0")
}
return a / b, nil
}
func main() {
result, err := divide(10, 0)
if err != nil {
fmt.Println("计算失败:", err)
return
}
fmt.Println("计算结果:", result)
}
运行上述代码会输出计算失败: 除数不能为0,可以看到errors.New创建的错误可以直接通过error类型返回,调用方通过判断err是否为nil来处理错误。
错误判断:使用errors.Is函数
当我们需要判断错误是否是指定的错误实例时,不能直接用==比较,尤其是在错误被包装的场景下,应该使用标准库的errors.Is函数。
示例代码如下:
package main
import (
"errors"
"fmt"
)
// 定义一个基础错误变量
var ErrDivByZero = errors.New("除数不能为0")
func divide(a, b int) (int, error) {
if b == 0 {
return 0, ErrDivByZero
}
return a / b, nil
}
func main() {
_, err := divide(10, 0)
if errors.Is(err, ErrDivByZero) {
fmt.Println("捕获到除数为0的错误")
}
}
这种方式比直接比较错误字符串更可靠,也符合Golang的错误处理惯例。
错误包装:errors.Join与fmt.Errorf
在复杂的调用链中,我们可能需要给错误添加更多的上下文信息,这时候可以使用错误包装功能,Golang 1.20之后提供了errors.Join函数,也可以结合fmt.Errorf的%w动词实现包装。
使用fmt.Errorf包装错误
package main
import (
"errors"
"fmt"
)
var ErrConfigMissing = errors.New("配置文件缺失")
func loadConfig(path string) error {
// 模拟配置文件不存在的场景
return fmt.Errorf("加载路径%s的配置失败: %w", path, ErrConfigMissing)
}
func main() {
err := loadConfig("/etc/app/config.yaml")
if err != nil {
fmt.Println("错误信息:", err)
// 依然可以通过errors.Is判断原始错误
if errors.Is(err, ErrConfigMissing) {
fmt.Println("原始错误是配置文件缺失")
}
}
}
使用errors.Join合并多个错误
当一次操作可能产生多个错误时,可以使用errors.Join将多个错误合并为一个:
package main
import (
"errors"
"fmt"
)
func validateInput(name string, age int) error {
var errs []error
if name == "" {
errs = append(errs, errors.New("姓名不能为空"))
}
if age < 0 {
errs = append(errs, errors.New("年龄不能为负数"))
}
if len(errs) > 0 {
return errors.Join(errs...)
}
return nil
}
func main() {
err := validateInput("", -1)
if err != nil {
fmt.Println("校验错误:", err)
}
}
自定义错误类型
如果需要在错误中携带更多的自定义信息,比如错误码、错误时间等,可以自定义实现error接口的结构体。error接口只有一个Error() string方法,只要结构体实现这个方法就可以作为错误类型使用。
package main
import (
"fmt"
"time"
)
// 自定义错误结构体
type AppError struct {
Code int
Message string
Time time.Time
}
// 实现error接口的Error方法
func (e *AppError) Error() string {
return fmt.Sprintf("错误码:%d, 信息:%s, 发生时间:%s", e.Code, e.Message, e.Time.Format("2006-01-02 15:04:05"))
}
func doBusiness() error {
// 返回自定义错误实例
return &AppError{
Code: 500,
Message: "数据库连接失败",
Time: time.Now(),
}
}
func main() {
err := doBusiness()
if err != nil {
fmt.Println("业务错误:", err)
// 可以通过类型断言获取自定义错误的额外信息
if appErr, ok := err.(*AppError); ok {
fmt.Printf("错误码是:%dn", appErr.Code)
}
}
}
使用注意事项
- 不要频繁使用
errors.New创建相同的错误字符串,建议将常用的错误定义为包级别的变量,方便统一管理和判断。 - 错误信息的描述要清晰,尽量包含足够的上下文,方便后续排查问题。
- 包装错误时不要丢失原始错误的类型信息,否则
errors.Is和errors.As可能无法正常工作。
掌握好errors包的使用,能让Golang项目的错误处理更加规范,也能降低后续维护的成本,建议在实际开发中多结合具体场景练习相关用法。