go 的 error 接口仅含 func error() string 方法,任何实现该方法的类型即为 error;errors.new 和 fmt.errorf(无 %w)生成基础错误,%w 才构建可穿透的错误链,支持 errors.is/as 判断。

Go 的 error 接口到底长什么样
它就一个方法:func Error() string,没有其他字段、没有嵌套、不强制实现任何额外行为。这意味着只要类型实现了这个方法,它就是 error——哪怕只是返回硬编码字符串。
常见错误是以为 error 是某种特殊类型或结构体,结果自己造轮子写 type MyError struct { Msg string; Code int } 却忘了实现 Error() 方法,导致编译报错 cannot use ... as error value in return statement: missing method Error。
- 标准库的
errors.New("xxx")返回的是*errors.errorString,内部只存字符串 -
fmt.Errorf("xxx")默认也返回同一种底层类型,除非用了%w包裹才产生包装错误 - 不要为简单错误定义新结构体,除非你需要携带额外字段(如 HTTP 状态码)且明确要参与错误判断
什么时候该用 fmt.Errorf,什么时候该用 errors.New
两者都生成基础 error,但语义和后续处理能力不同。
errors.New 适合无上下文、不可分解的原子错误,比如 errors.New("EOF");fmt.Errorf 更适合带参数、需拼接上下文的错误,比如读文件时带上文件名:fmt.Errorf("failed to read %s: %w", filename, err)。
立即学习“go语言免费学习笔记(深入)”;
在原版的基础上做了一下修正评论没有提交正文的问题特价商品的调用连接问题去掉了一个后门补了SQL注入补了一个过滤漏洞浮动价不能删除的问题不能够搜索问题收藏时放入购物车时出错点放入购物车弹出2个窗口修正主题添加问题商家注册页导航连接问题销售排行不能显示更多问题热点商品不能显示更多问题增加了服务器探测 增加了空间使用查看 增加了在线文件编辑增加了后台管理里两处全选功能更新说明:后台的部分功能已经改过前台
- 如果错误需要被
errors.Is或errors.As检查,优先用fmt.Errorf配合%w包裹原始错误 - 纯字符串拼接(不用
%w)和errors.New效果等价,但前者可读性更好 - 避免
fmt.Errorf("something went wrong: %v", err)这种写法——它丢弃了原始错误的类型信息,导致无法用errors.As提取底层错误
errors.Is 和 errors.As 判断失败的典型原因
这两个函数依赖错误链(error wrapping),不是靠字符串匹配,也不是靠指针相等。
最常踩的坑是:用 errors.New 或未用 %w 的 fmt.Errorf 包装错误后,再调 errors.Is(err, io.EOF) 就永远返回 false。
- 只有通过
%w显式包装的错误才构成错误链,比如fmt.Errorf("read failed: %w", io.EOF) -
errors.Is会逐层解包,检查每一层是否与目标错误==或Is()成立 -
errors.As要求目标变量是指针(如&target),否则 panic:interface conversion: error is *fmt.wrapError, not *os.PathError
自定义错误类型时绕不开的两个细节
如果你真要定义自己的错误类型(比如带状态码、请求 ID),必须同时满足两件事:实现 Error() 方法 + 支持错误包装。
否则它在错误链里就“断”了,上游无法用 errors.Is 或 errors.As 触达你塞进去的字段。
- 实现
Unwrap() error方法才能让errors.Is/As向下穿透,返回你要包装的底层错误 - 如果还想保留自己的字段(如
Code int),得在Unwrap()里返回被包装的错误,在Error()里把字段和消息一起格式化 - 别忘了导出字段(首字母大写),否则外部包无法访问你的状态码或追踪 ID
%w 不是可选语法糖,而是决定你能否做类型判断的关键开关。









