必须用具名类型(如type usererrorcode int)定义错误码,从iota+1000起始,绑定apperror结构体并实现error/unwrap方法,通过工厂函数创建,用go:generate自动生成文档与跨语言映射。

Go 项目里错误码散落在各处、靠字符串匹配或裸 int 比较,不出三个月就会出现 ErrUserNotFound 在 user 包是 1001,在 auth 包变成 2001,前端解析直接崩溃——这不是偶然,是没用具名类型约束 + 没做枚举定义 + 没生成可交付文档。
用自定义类型替代 int 定义错误码
直接用 const ErrNotFound = 404 看似省事,但 404 可能被误传给数据库层、HTTP 状态码函数甚至日志字段,编译器完全无法拦截。必须用具名类型隔离语义:
-
type ErrorCode int是底线;更推荐type UserErrorCode int(按模块细分) - 所有码值从
iota + 1000起始,避开系统错误(如os.ErrNotExist是 -2) - 禁止导出裸 int 值,只暴露带类型的常量:
var ErrUserNotFound UserErrorCode = iota + 1000 -
func (e UserErrorCode) Code() int提供统一取值入口,中间件和日志器只认这个方法
错误码必须和 error 接口绑定,不能只定义枚举
只定义 ErrUserNotFound 常量没用——调用方仍要手动拼 fmt.Errorf("user not found: %w", err),一包就丢码。必须让错误实例本身携带码:
- 定义结构体如
type AppError struct { Code ErrorCode; Message string; Err error } - 实现
Error()返回Message,不拼接码值(避免日志里重复出现[1001] user not found) - 必须实现
Unwrap(),否则errors.As(err, &AppError{})无法穿透包装获取原始码 - 工厂函数强制接管创建:用
user.NewErrNotFound(),禁用fmt.Errorf直接构造
用 go:generate 自动生成文档与跨语言映射
人工维护错误码文档必然过期。真实项目里,错误码定义文件(如 pkg/code/user.go)应同时作为文档源和代码生成输入:
- 在常量定义上方加
//go:generate go run gen_errors.go - 脚本读取 AST,提取所有
UserErrorCode常量及其注释(如// 用户不存在),生成 Markdown 表格和 JSON 映射 - 输出含三列:
Code、ConstName、ChineseMsg,前端可直接导入为 enum 或 i18n key - Flutter/Dart/TS 侧用相同脚本生成对应枚举,保证前后端码值绝对一致,不用人工对齐
最容易被忽略的是:错误码文档不是写完就扔的静态文件,而是每次 go generate 都要重新跑一遍的构建步骤——只要改了 ErrUserNotFound 的值或注释,文档和客户端代码就自动同步。没这一步,再规范的枚举定义也撑不过两个迭代。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











