go 的 error 接口不能直接比较值,因为其为接口类型,底层可能指向不同结构体实例,即使内容相同,== 比较也常返回 false;应使用 errors.is 或 errors.as 进行健壮判断。

Go 的 error 接口为什么不能直接比较值?
因为 error 是接口类型,底层可能指向不同结构体实例,哪怕内容一样,== 比较也常返回 false。比如两个 fmt.Errorf("not found") 创建的 error,地址不同,值比较就失效。
- 用
errors.Is(err, target)判断是否为某类错误(支持包装链) - 用
errors.As(err, &target)提取底层具体错误类型(如自定义 struct) - 避免写
if err == errors.New("xxx")或if err.Error() == "xxx"—— 既脆弱又无法处理包装错误 - 如果必须做字符串匹配(调试或日志),先确认没其他更健壮的方式可用
什么时候该用 fmt.Errorf 带 %w 包装错误?
当你需要在不丢失原始错误语义的前提下添加上下文时,比如从数据库层透出到 HTTP handler 层,又不想让调用方失去重试、分类或日志追踪能力。
- 只在确实要“加一层上下文”时用
%w,不是所有错误都要包装 -
%w只能出现在fmt.Errorf的最后一个参数,且只能有一个 - 包装后原错误仍可通过
errors.Unwrap或errors.Is访问,但过度嵌套会拖慢判断速度(一般不超过 3 层) - 示例:
return fmt.Errorf("failed to save user %d: %w", id, err)
自定义错误类型要不要实现 Error() 和 Unwrap()?
取决于你是否需要被 errors.Is/errors.As 正确识别,以及是否要参与错误链传递。
- 只要实现了
Error() string就是合法error;但若想被%w包装并保留类型信息,还得加Unwrap() error - 如果错误携带结构化字段(如
StatusCode int),建议实现As(target interface{}) bool,方便外部提取 - 不要在
Unwrap()里返回nil以外的固定值——这会让错误链断裂或误导判断 - 常见反例:定义了 struct 却只实现
Error(),结果上层用errors.As提取失败
HTTP handler 中怎么把 Go 错误转成合适的 HTTP 状态码?
不能靠错误字符串关键字匹配,得靠类型或预设标记,否则一改提示文案就崩。
立即学习“go语言免费学习笔记(深入)”;
- 定义一组带状态码的错误变量,如
var ErrNotFound = &statusError{code: 404, msg: "not found"} - 在 handler 末尾统一用
switch或errors.As匹配已知错误类型,再设置w.WriteHeader(code) - 对未知错误,统一返回 500 并记录日志,避免泄露敏感信息
- 别在每个 handler 里重复写
if errors.Is(err, os.ErrNotExist)—— 抽成中间件或辅助函数更可控










