errors.New返回的错误不能直接比较相等,因其每次调用都创建新指针实例,故err == errors.New("x")恒为false;应使用errors.Is、自定义类型或谨慎用err.Error()。

errors.New 为什么返回的错误不能直接比较相等?
errors.New 返回的是一个私有结构体指针,每次调用都生成新实例,所以用 == 或 == nil 判断是否为某个特定错误会失败。常见误写:
if err == errors.New("timeout") { ... } 这永远为 false —— 因为左右两边是两个独立分配的错误对象。
正确做法是用 errors.Is(Go 1.13+)或自定义错误类型 + errors.As。若只需简单字符串匹配,可用 err.Error() == "timeout",但不推荐用于生产环境,因易受消息变更影响。
什么时候该用 errors.New 而不是 fmt.Errorf?
errors.New 适合创建无参数、固定文本的静态错误,比如 errors.New("connection refused");fmt.Errorf 更适合带变量插值的场景,例如 fmt.Errorf("failed to read file %q: %w", filename, io.ErrUnexpectedEOF)。
关键区别在于:
-
errors.New不支持格式化,也不支持错误链(%w) -
fmt.Errorf支持%w实现嵌套,便于后续用errors.Unwrap或errors.Is检查底层原因 - 两者返回的都是
error接口,可互换使用,但语义不同
如何让 errors.New 创建的错误支持 Is 和 As?
原生 errors.New 返回的错误不实现 Unwrap() 方法,因此无法参与错误链判断。若需让它被 errors.Is 识别,必须包装或自定义类型:
立即学习“go语言免费学习笔记(深入)”;
type MyError struct{ msg string }
func (e *MyError) Error() string { return e.msg }
func (e *MyError) Is(target error) bool {
return target == ErrTimeout || target == e
}
var ErrTimeout = &MyError{"timeout occurred"}
或者更轻量地用 fmt.Errorf 配合 %w 包装:
var ErrTimeout = fmt.Errorf("timeout occurred") // 不带 %w,行为类似 errors.New
// 若需链式,应显式定义:
var ErrTimeout = fmt.Errorf("timeout occurred: %w", context.DeadlineExceeded)
errors.New 在 HTTP handler 中直接返回是否安全?
不安全。直接把 errors.New("invalid ID") 返回给前端,容易暴露内部逻辑或调试信息。实际 Web 服务中应做转换:
- 用中间件统一拦截
error并转成 JSON 响应(如{"error": "Bad Request"}) - 区分用户错误与系统错误:用户错误用预定义变量(
ErrNotFound = errors.New("not found")),系统错误加日志后转为 500 - 避免在错误消息中拼接用户输入,防止注入或信息泄露
典型反例:
http.Error(w, errors.New("user " + r.URL.Query().Get("id") + " not found").Error(), http.StatusNotFound) —— 用户可控输入进了错误消息,风险高。
错误链支持和语义明确性比“一行创建”更重要;别为了省一个 fmt. 前缀而放弃可诊断性。










