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

如何使用Golang errors New创建错误_Golang基础错误构建方法  第1张

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.Unwraperrors.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. 前缀而放弃可诊断性。