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):只保留错误文本,丢失原始错误类型