Go中创建自定义错误类型需实现error接口(Error() string方法),可添加字段、Unwrap()支持错误链,按需实现Is()/As()增强识别,推荐用工厂函数和错误码统一管理。

在Go中创建自定义错误类型,核心是让结构体实现 error 接口(即拥有 Error() string 方法),同时可根据需要添加字段、方法和错误分类能力。
基础方式:结构体 + Error() 方法
最常用也最清晰的做法是定义一个结构体,内嵌或组合所需信息(如错误码、上下文、原始错误等),再实现 Error() 方法返回可读字符串。
- 结构体字段可包含
message、code、timestamp、cause error等,便于调试和分级处理 -
Error()方法应返回简洁明确的描述,避免暴露敏感信息;若需详细日志,可用额外方法(如Detail())提供 - 不要在
Error()中做耗时操作(如网络请求、格式化大对象),它可能被频繁调用
支持错误链:嵌入并包装底层错误
Go 1.13+ 原生支持错误链(via errors.Unwrap 和 %w 动词),自定义错误可通过字段保存原始错误,并实现 Unwrap() 方法使其参与链式判断。
- 定义字段如
err error存储上游错误 - 实现
Unwrap() error { return e.err }即可被errors.Is/errors.As正确识别 - 在
Error()中使用fmt.Sprintf("xxx: %v", e.err)或fmt.Sprintf("xxx: %w", e.err)(后者保留链)
支持错误识别:添加 Is() 和 As() 方法(按需)
若希望自定义错误能被 errors.Is 精确匹配(比如判断是否为“数据库连接超时”),可实现 Is(target error) bool;若需类型断言提取数据,可实现 As(target interface{}) bool。
-
Is()通常用于比对已知错误值(如全局变量ErrTimeout)或检查错误码字段 -
As()多用于将错误解包到目标结构体指针,常配合反射或类型断言实现 - 这两个方法不是必须的,仅在需要细粒度错误判断逻辑时才添加
实用建议与常见模式
实际项目中推荐结合使用标准库与约定,提升可维护性。
- 错误码统一管理:用
const定义错误码(如ErrCodeDBConnection = "DB001"),结构体中只存码而非字符串 - 工厂函数封装创建逻辑:如
NewDBError(code, msg, err),避免直接 new 结构体 - 避免过度设计:简单场景用
fmt.Errorf("xxx: %w", err)或errors.New("xxx")足够;仅当需携带结构化信息或定制行为时才写自定义类型 - 日志与错误分离:
Error()返回用户/运维友好的提示;详细上下文(如SQL、参数)应单独记录,不塞进错误字符串
基本上就这些。Go 的错误机制强调显式传递和组合,而不是继承或异常捕获。自定义 error 的关键不是“多高级”,而是“是否方便判断、是否携带必要上下文、是否易于测试和维护”。










