0%

Go错误处理速查表

Go错误处理速查表

一、三种错误形态与判断方式

# 形态 怎么产生 适用场景 怎么判断
1 哨兵错误(sentinel) var ErrNotFound = errors.New("not found") 调用方只需知道“是不是这个错”,不需要额外信息 errors.Is(err, ErrNotFound)(推荐)
err == ErrNotFound(仅当确定未被包裹)
2 包裹错误(wrap,加上下文) fmt.Errorf("load user %d: %w", id, err) 往上传递时补充“在哪、在做什么” errors.Is(err, ErrNotFound) 沿链查找值
3 自定义错误类型(struct) type QueryError struct{...} 实现 Error() string 调用方需要读取错误里的字段(状态码、字段名、重试时间等) e, ok := errors.AsType[*QueryError](err)(Go 1.26+)
var e *QueryError; errors.As(err, &e)(老版本)

比较错误值用 Is,提取错误类型用 As/AsType。

二、代码模板

1. 哨兵错误

1
2
3
4
5
6
7
8
var ErrNotFound = errors.New("not found")   // 包级变量,导出给调用方比较

func Find(id int) (*User, error) {
return nil, ErrNotFound
}

if errors.Is(err, ErrNotFound) { ... } // 推荐
if err == ErrNotFound { ... } // 只在确定没被 wrap 时可用

2. 包裹错误

1
2
3
4
5
6
if err != nil {
return fmt.Errorf("find user %d: %w", id, err) // %w 保留错误链
}

// 判断:第一个参数是拿到的 err,第二个参数是目标【错误值】(不是类型)
if errors.Is(err, ErrNotFound) { ... }

3. 自定义错误类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
type QueryError struct {
Query string
Err error
}
func (e *QueryError) Error() string { return e.Query + ": " + e.Err.Error() }
func (e *QueryError) Unwrap() error { return e.Err } // 让 Is/As 能继续往里找

return &QueryError{Query: q, Err: ErrNotFound}

// Go 1.26+:泛型版,参数是【类型】
if qe, ok := errors.AsType[*QueryError](err); ok {
log.Println(qe.Query)
}

// Go 1.25 及以前
var qe *QueryError
if errors.As(err, &qe) { log.Println(qe.Query) }

三、%w 还是 %v

用法 效果 什么时候用
%w 保留错误链,调用方可 Is/As 希望调用方能识别底层错误
%v 只拼字符串,断开链 不想把底层错误暴露成你的 API(如隐藏所用的数据库驱动)

%w 不是必须的。用了它,底层错误就成了对外契约的一部分,所以只在希望调用方判断底层错误时使用。

四、其他要点

  • 不要比较错误字符串:不要写 strings.Contains(err.Error(), "...")。
  • 多个错误:errors.Join(err1, err2),或 fmt.Errorf("...: %w, %w", e1, e2)(Go 1.20+),Is/As 会逐个检查。
  • 只处理一次:要么 log,要么 return。既打日志又往上抛,同一个错误会被记录多次。
  • 上下文措辞:小写开头、不加标点、不写 “failed to”,例:"open config: %w"。
  • 自定义类型用指针接收者,AsType/As 时也用指针类型 *QueryError,二者要一致。
  • 不要返回有类型的 nil:var e *QueryError = nil; return e 得到的 error 不等于 nil。
  • 自定义匹配:类型可实现 Is(target error) bool 改变 errors.Is 的判定逻辑(少用)。
  • 哨兵错误命名:ErrXxx;错误类型命名:XxxError。

五、决策流程