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 不会保留错误链)