Go错误须用errors.New/fmt.Errorf构造,禁用字符串比较;用errors.Is/As判断和提取错误;统一定义基础错误变量;避免敏感信息入错;必检err!=nil并明确处理策略;包装用%w保留错误链;HTTP错误应结构化返回。

错误值必须用 errors.New 或 fmt.Errorf 显式构造,别用字符串直接比较
Go 的错误本质是接口,error 值不能靠 == 判断内容是否相等——底层指针可能不同。常见错误是写 if err == "not found",这永远为 false。
- 用
errors.Is(err, someErr)判断是否是某类错误(比如自定义的ErrNotFound) - 用
errors.As(err, &target)提取具体错误类型(比如提取*os.PathError) - 团队应提前定义一组基础错误变量,如
var ErrNotFound = errors.New("record not found"),所有模块复用,不重复fmt.Errorf("record not found") - 避免在错误信息里拼接敏感数据(如用户 ID、路径),日志里再补上下文更安全
函数返回错误时,必须检查 err != nil,且不能只打日志就继续往下走
Go 不强制处理错误,但跳过检查等于默认“这个错误可以忽略”,线上最常见 panic 往往源于未校验的 nil 指针或空切片,根源却是上游错误没拦截。
- 每个
err返回后,第一件事是判断:不是所有地方都要return,但至少要决定“继续执行”还是“终止流程” - 不要写
if err != nil { log.Println(err); continue }—— 这会让错误静默传播,下游拿到空数据却不知为何 - 如果确实要忽略(极少数场景,如
os.Remove删除不存在的文件),显式写成if !os.IsNotExist(err) { return err },表明你清楚在忽略什么
包装错误要用 fmt.Errorf("%w", err),别用 %v 或字符串拼接
用 %w 才能保留原始错误链,让 errors.Is 和 errors.As 正常工作;用 %v 就断了链,只剩一串文本。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 正确:
return fmt.Errorf("failed to parse config: %w", err) - 错误:
return fmt.Errorf("failed to parse config: %v", err)(丢失原始类型) - 错误:
return errors.New("failed to parse config: " + err.Error())(彻底扁平化) - 包装层级建议 ≤3 层:入口层(业务语义)、中间层(模块语义)、底层(系统/IO 错误)。太深会增加调试成本
HTTP handler 中错误要转成结构化响应,别直接 http.Error 或裸奔 panic
API 错误不是程序崩溃,用户需要明确 code、message、可选 detail;而 http.Error 只给 status+text/plain,前端难解析,监控也抓不到分类。
- 统一用一个
ErrorResponse结构体,含Code(业务码,如 "USER_NOT_FOUND")、Message、RequestID - handler 里遇到错误,先
log.Errorw("handler failed", "err", err, "req_id", reqID),再序列化ErrorResponse写回 - 别在 middleware 里 recover panic 后塞个通用 500——要区分是编程错误(该修代码)还是预期外请求(该返回 400)
- 第三方库报错(如
json.Unmarshal)通常对应 400;数据库查不到对应 404;权限不足对应 403;内部逻辑失败才考虑 500
最难统一的其实是“什么时候该加一层包装,什么时候该透出原始错误”。这取决于调用方是否需要感知底层细节——大多数业务层函数,应该只暴露它自己定义的错误语义,而不是把 io.EOF 直接甩给上层。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










