go错误处理强调显式传递与上下文包装:必须检查error,用fmt.errorf %w包装、errors.join合并并发错误,自定义类型适用于需结构化判断的场景。
错误传递必须显式处理,Go 没有隐式异常传播
Go 的设计哲学是“错误即值”,error 是接口类型,不是异常。函数返回 error 后,调用方**必须检查并决定如何处理**——不检查、不返回、不包装,错误就丢了。常见现象是:深层调用出错,但上层日志只显示 "operation failed",无堆栈、无上下文、无法定位。
- 不要用
if err != nil { return err }一招鲜走天下,尤其在需要补充上下文时 - 避免在中间层吞掉错误(如
if err != nil { log.Println(err); return nil }),这等于主动放弃调试线索 - 不要用
panic替代错误传递,除非是真正的不可恢复程序崩溃(如配置解析失败且无法降级)
用 fmt.Errorf + %w 实现带链路的错误包装
从 Go 1.13 开始,fmt.Errorf 支持 %w 动词,用于包裹底层错误,形成可追溯的错误链。这是目前最轻量、标准库原生支持的传播方式。
func readFile(path string) error {
data, err := os.ReadFile(path)
if err != nil {
return fmt.Errorf("failed to read config file %q: %w", path, err)
}
// ...
return nil
}
-
%w只能出现在格式字符串末尾,且仅接受一个error类型参数 - 被
%w包裹的错误可通过errors.Unwrap或errors.Is/errors.As向下提取 - 多次
%w包装会形成链,但不会自动记录调用位置;需配合runtime.Caller或第三方库补全栈信息
用 errors.Join 合并多个并发错误
当多个 goroutine 并行执行可能出错的操作(如批量写入多个服务),需要把所有错误聚合成一个统一返回值时,errors.Join 是 Go 1.20+ 提供的标准方案。