errors.New 返回的是实现了 error 接口的私有值类型 errors.errorString,仅含字符串字段和 Error() 方法,不可扩展、无堆栈、不支持错误链,适用于固定不变的包级错误标识。

errors.New 生成的错误类型到底是什么
errors.New 返回的是一个实现了 error 接口的私有结构体(errors.errorString),不是指针也不是自定义类型,而是一个带字符串字段的值类型。它只实现了一个方法:Error(),返回初始化时传入的字符串。
这意味着你不能对它做类型断言成其他自定义错误类型,也不能直接添加字段或行为——它就是最轻量的错误容器。
- 它和
fmt.Errorf("...")返回的类型不同(后者在 Go 1.13+ 是*fmt.wrapError,支持链式错误) - 它不支持
errors.Is或errors.As的错误匹配(除非你用它本身做目标值) - 多次调用
errors.New("EOF")会得到两个不相等的错误值(==判定为 false),因为是不同实例
什么时候该用 errors.New 而不是 fmt.Errorf
当你只需要一个固定、无参数、不可变的错误标识时,errors.New 更清晰、更轻量。
-
标准库中大量使用:比如
io.EOF = errors.New("EOF")、http.ErrUseLastResponse = errors.New("http: use last response") - 定义包级错误变量时推荐:
var ErrInvalidID = errors.New("invalid user ID") - 避免拼接字符串错误(如
"failed to open " + filename)——这种场景必须用fmt.Errorf,否则丢失上下文 - 不要在循环里反复调用
errors.New生成相同错误,应提前定义为变量复用
errors.New 错误无法被 errors.Is 匹配?真相是……
它可以被 errors.Is 匹配,但仅限于「完全相同的错误值」或「底层错误链中存在该值」。由于 errors.New 返回的是值类型,且不嵌套其他错误,所以:
立即学习“go语言免费学习笔记(深入)”;
-
errors.Is(err, ErrInvalidID)✅ 成立,前提是err就是那个变量(或用==比较也成立) -
errors.Is(fmt.Errorf("wrap: %w", ErrInvalidID), ErrInvalidID)✅ 成立(Go 1.13+ 支持%w,会构建错误链) -
errors.Is(errors.New("invalid user ID"), ErrInvalidID)❌ 不成立,即使字符串一样,也是两个独立值 - 别试图用
errors.Is(err, errors.New("xxx"))做运行时判断——每次都会新建一个错误,永远不匹配
如何安全地暴露 errors.New 错误给外部使用者
对外暴露错误变量时,必须导出(首字母大写),但绝不暴露其具体类型——只暴露为 error 接口。
- ✅ 正确:
var ErrNotFound = errors.New("not found")(导出变量名,类型是error) - ❌ 错误:
type ErrNotFound error再赋值(造成类型泄漏,破坏接口抽象) - 如果需要区分多个同类错误(比如不同资源的 NotFound),建议用
fmt.Errorf("user not found: %s", id)或自定义类型实现Unwrap()和Is() - 注意文档注释要说明该错误的触发条件,因为使用者无法从
errors.New字符串里推断上下文
真正容易被忽略的是:errors.New 创建的错误没有任何堆栈信息,也不支持延迟注入上下文。一旦你需要调试或日志追踪,就得换用 fmt.Errorf 或第三方错误包(如 github.com/pkg/errors)。










