该用 errors.new 处理静态错误,fmt.errorf(配合 %w)处理需动态值或错误包装的场景;%w 是唯一能保留错误链的方式,支持 errors.is 和 errors.as 正常工作,而 %v 或 %s 会丢失原始错误信息。

errors.New 和 fmt.Errorf 哪个该用?
直接创建简单错误时用 errors.New,带动态值或上下文时用 fmt.Errorf。前者返回一个不可变的、无格式化的错误;后者支持格式化字符串,但默认不保留原始错误(除非用 %w 动词)。
常见错误是以为 fmt.Errorf("failed: %v", err) 能保留栈或嵌套关系——其实不能,它只是把 err.Error() 拼进去,原始错误被“扁平化”了。
- 静态消息(如参数校验失败):用
errors.New("invalid ID") - 需要插入变量或调用链信息:用
fmt.Errorf("fetch user %d failed: %w", id, err) - 避免写
fmt.Errorf("failed: %s", err.Error())—— 这和errors.New(err.Error())几乎等价,丢失所有类型和包装信息
如何正确包装错误并保留原始错误?
用 %w 是唯一标准方式。它让 errors.Unwrap、errors.Is、errors.As 能正常工作。没有 %w,错误就断链了。
例如:fmt.Errorf("database query failed: %w", dbErr) 包装后,你可以用 errors.Is(err, sql.ErrNoRows) 判断是否是空结果,前提是 dbErr 本身也用了 %w 或是原生错误。
立即学习“go语言免费学习笔记(深入)”;
动态WEB网站中的PHP和MySQL详细反映实际程序的需求,仔细地探讨外部数据的验证(例如信用卡卡号的格式)、用户登录以及如何使用模板建立网页的标准外观。动态WEB网站中的PHP和MySQL的内容不仅仅是这些。书中还提到如何串联JavaScript与PHP让用户操作时更快、更方便。还有正确处理用户输入错误的方法,让网站看起来更专业。另外还引入大量来自PEAR外挂函数库的强大功能,对常用的、强大的包
- 只在你想让调用方能检测/提取底层错误时才用
%w - 不要对非错误类型(比如
string或int)用%w,会 panic - 多个包装层级没问题,但每层都必须用
%w,否则链在某一层就断了
errors.Is 和 errors.As 的实际使用边界
errors.Is 检查的是“是否等于某个目标错误值”,常用于判断是否是预定义错误(如 io.EOF、os.ErrNotExist)。它会逐层 Unwrap 直到匹配或为 nil。
errors.As 用于类型断言,想拿到被包装的底层错误实例(比如提取 *os.PathError 中的 Path 字段)。它也递归查找,但只找第一个匹配的类型。
-
errors.Is(err, os.ErrNotExist)安全,因为os.ErrNotExist是包级变量,可比较 - 别写
errors.Is(err, errors.New("not found"))—— 每次errors.New都是新对象,永远不相等 -
errors.As(err, &pathErr)成功后,pathErr指向被包装的*os.PathError实例,可直接访问字段
自定义错误类型要不要实现 Unwrap 方法?
要,但仅当你的错误类型内部持有另一个错误且希望它参与标准错误链时。Go 标准库只认名为 Unwrap() error 的方法(签名必须完全一致),其他名字或返回多个值都不行。
注意:如果你的自定义错误只是加了字段(如 Code int、TraceID string),但不包裹其他错误,就不需要 Unwrap;如果它像 type MyErr struct { Msg string; Cause error },那就必须实现 func (e *MyErr) Unwrap() error { return e.Cause }。
- 没实现
Unwrap的自定义错误会被errors.Is当作叶子节点,无法穿透 - 返回
nil表示链结束,返回非nil错误才会继续向下检查 - 不要在
Unwrap里做计算或 I/O,它可能被频繁调用
fmt.Errorf 里用 %w,或者写了 %v 却以为自己在包装。









