
Go 里 error 接口为什么不能加方法?
因为标准库的 error 就是 interface{ Error() string },任何额外方法都会破坏兼容性。你写的 MyError 类型如果实现了 Error() 和 StatusCode(),它确实满足 error 接口,但调用方只认 Error() —— 其他方法必须显式类型断言才能用。
- 常见错误现象:
if e, ok := err.(CustomErr); ok { return e.StatusCode() }写得到处都是,一不小心就 panic - 正确做法:把状态码、重试建议等元信息塞进错误消息里(比如用
fmt.Errorf("failed to fetch user: %w; status=404", err)),或统一用包装器如errors.Join/fmt.Errorf("%w", err) - 性能影响:类型断言本身开销小,但分散的断言逻辑会让错误处理路径难以维护,尤其在中间件或日志层
HTTP handler 中怎么返回结构化错误而不暴露内部细节?
别直接 return fmt.Errorf("db: %v", err),更别把 err.Error() 原样塞进 JSON 响应体。用户不需要知道是 PostgreSQL 还是 SQLite 报错,只需要知道“请求失败,请稍后重试”。
- 使用场景:API 返回
400 Bad Request或500 Internal Server Error时,响应体中的message字段必须可控 - 实操建议:
switch匹配错误类型(如*json.UnmarshalTypeError→400),再用预定义错误码映射表生成用户友好提示 - 容易踩的坑:用
errors.Is(err, io.EOF)判断网络中断没问题,但用strings.Contains(err.Error(), "timeout")就不可靠——不同驱动报错字符串不一致
errors.As 和 errors.Is 的实际区别在哪?
errors.Is 查的是错误链里的“相等性”,比如是否等于某个哨兵错误;errors.As 查的是“可转换性”,用来提取底层具体错误类型。
- 典型误用:
if errors.As(err, &net.OpError{})写成if errors.Is(err, &net.OpError{})—— 后者永远 false,因为Is比较的是值相等,不是类型匹配 - 参数差异:
As第二个参数必须是指针变量(&e),否则 panic;Is第二个参数是具体值或哨兵变量(io.EOF) - 兼容性注意:Go 1.20+ 支持自定义错误实现
Unwrap()方法构建错误链,但老版本驱动(如旧版pgx)可能没实现,As会失败
要不要给每个业务错误定义一个新类型?
不要。90% 的业务错误用哨兵错误(var ErrNotFound = errors.New("not found"))或带字段的结构体(type ValidationError struct{ Field, Msg string })就够了。过度抽象只会让调用方写一堆无意义的 if errors.As(...)。
立即学习“go语言免费学习笔记(深入)”;
- 什么时候该定义新类型?只有当你需要在错误里携带**不可序列化的上下文**(比如数据库连接句柄)或**必须重写
Error()行为**(比如自动打日志)时才考虑 - 性能影响:结构体错误比字符串错误多一次内存分配,但只要不高频 panic,影响可忽略
- 容易被忽略的点:所有自定义错误类型都该实现
Unwrap() error(哪怕返回 nil),否则errors.Is/As在嵌套时失效










