文章

errors规则

核心结论

写法作用是否保留错误链常见用途
%v打印错误文本日志、简单拼接
%+v打印更详细格式否,取决于 error 类型配合 pkg/errors 打印堆栈
%w包装 error给错误加上下文,并让上层还能判断原始错误, 支持errors.Is()
errors.Wrap第三方包装 error老项目常见,可配合 %+v 打印堆栈
errors.Join合并多个 error多个独立操作都可能失败,需要一起返回

最常用:fmt.Errorf + %w

errors.Is() 使用场景总结

✅ 可以使用的情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
1. 直接返回预定义错误实例 // 定义
var ErrNotFound = errors.New("not found")

// 返回
func GetUser(id string) error {
    return ErrNotFound  // ✅ 直接返回同一个实例
}

// 判断
if errors.Is(err, ErrNotFound) {  // ✅ 返回 true
2. 通过 fmt.Errorf 包装的错误链
// 定义
var ErrNotFound = errors.New("not found")

// 返回(Go 1.13+ 支持 %w)
func GetUser(id string) error {
    err := ErrNotFound
    return fmt.Errorf("get user failed: %w", err)  // ✅ 使用 %w 包装
}

// 判断
if errors.Is(err, ErrNotFound) {  // ✅ 返回 true(会解包查找)


3. 多层包装, 但如果中间任何一层没有使用 %W链就断了
var ErrNotFound = errors.New("not found")

func layer1() error {
    return fmt.Errorf("layer1: %w", ErrNotFound)
}

func layer2() error {
    return fmt.Errorf("layer2: %w", layer1())
}
// 判断
err := layer2()
fmt.Println(err)
// layer2: layer1: not found
fmt.Println(errors.Is(err, ErrNotFound)) // ✅ 即使包装多层也能找到
// true

❌ 不能使用的情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1. 使用 errors.New() 创建新实例(你的代码中的情况)
var ErrNotFound = errors.New("not found")

func GetUser(id string) error {
    return errors.New("not found")  // ❌ 创建了新的错误实例
}

// 判断
if errors.Is(err, ErrNotFound) {  // ❌ 返回 false(不是同一个实例)

2. 使用 fmt.Errorf 但不带 %w
var ErrNotFound = errors.New("not found")

func GetUser(id string) error {
    return fmt.Errorf("get user failed: %v", ErrNotFound)  // ❌ 使用 %v
}

// 判断
if errors.Is(err, ErrNotFound) {  // ❌ 返回 false(%v 不会保留错误链)

© 2024- lfj