Skip to main content

错误处理

显式 error 返回是 Go 的默认风格;panic 只用于真正异常路径。

error 与惯用写法

error 是单方法接口:Error() string。函数常返回 (T, error),调用方立刻检查:

f, err := os.Open("a.txt")
if err != nil {
return fmt.Errorf("open: %w", err)
}
defer f.Close()

不要忽略错误(_ = do() 除非你真的不在乎)。成功时 err == nil

自定义错误:

var ErrNotFound = errors.New("not found")

type QueryError struct {
Key string
Err error
}

func (e *QueryError) Error() string {
return fmt.Sprintf("query %s: %v", e.Key, e.Err)
}

包装与判断

%w 包装以保留错误链,再用 errors.Is / errors.As 判断:

func load(key string) error {
err := ErrNotFound
return fmt.Errorf("load %s: %w", key, err)
}

err := load("user")
if errors.Is(err, ErrNotFound) {
fmt.Println("missing")
}

var qe *QueryError
if errors.As(err, &qe) {
fmt.Println(qe.Key)
}
API用途
errors.Is(err, target)链上是否匹配某 sentinel / 可比较错误
errors.As(err, &target)链上是否有某具体类型
fmt.Errorf("...: %w", err)包装
errors.Join(1.20+)合并多个错误

只想附加信息、不参与 Is 链时,可用 %v(一般优先 %w)。

panic / recover 的边界

panic 中止当前 goroutine 的正常控制流;recover 只能在 defer 中调用以接住 panic:

func safe() (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("panic: %v", r)
}
}()
panic("boom")
}

建议:

  • 库代码:返回 error,不要 panic 表达可预期失败
  • 进程边界(如 HTTP 中间件、worker 顶层):可 recover,记日志,避免整个进程挂掉
  • 编程错误(如索引越界)本身会 panic——修复代码,而不是到处 recover

小结

  1. (T, error) + if err != nil 是主路径
  2. %w + errors.Is / As 做错误链
  3. sentinel(var ErrX = errors.New)与自定义类型按需选用
  4. panic/recover 留给真正异常与边界防护

上一篇:并发 · 下一篇:标准库常用