跳到主要内容

errors 错误处理进阶

问题

Go 1.13+ 的错误处理有哪些新特性?errors.Is 和 errors.As 怎么用?

答案

错误包装与链

Go 1.13 引入 %w 动词包装错误,形成错误链

originalErr := errors.New("file not found")
wrappedErr := fmt.Errorf("open config: %w", originalErr)
doubleWrapped := fmt.Errorf("init app: %w", wrappedErr)

// 错误链:init app -> open config -> file not found
fmt.Println(doubleWrapped)
// init app: open config: file not found

errors.Is——值比较

沿错误链查找特定错误值

if errors.Is(err, os.ErrNotExist) {
// err 或其链上的某个错误 == os.ErrNotExist
}

// 替代了之前的直接比较
if err == os.ErrNotExist { } // ❌ 不能检查包装过的错误

errors.As——类型断言

沿错误链查找特定错误类型

var pathErr *os.PathError
if errors.As(err, &pathErr) {
fmt.Println(pathErr.Path) // 获取具体信息
}

errors.Join(Go 1.20+)

合并多个错误:

err1 := errors.New("error 1")
err2 := errors.New("error 2")
combined := errors.Join(err1, err2)

errors.Is(combined, err1) // true
errors.Is(combined, err2) // true

自定义错误类型

type ValidationError struct {
Field string
Message string
}

func (e *ValidationError) Error() string {
return fmt.Sprintf("field %s: %s", e.Field, e.Message)
}

// 支持 errors.Is 自定义匹配逻辑
func (e *ValidationError) Is(target error) bool {
t, ok := target.(*ValidationError)
if !ok { return false }
return e.Field == t.Field
}

常见面试问题

Q1: errors.Is 和 == 的区别?

答案== 只比较当前错误,errors.Is递归遍历错误链(通过 Unwrap() 方法)直到找到匹配或到达链尾。

Q2: %w 和 %v 包装错误的区别?

答案

  • fmt.Errorf("...: %w", err):保留错误链,可以用 errors.Is/As 解包
  • fmt.Errorf("...: %v", err):只保留错误文本,丢失原始错误类型

相关链接