errors.Is常返回false,因其仅检查错误链中是否存在同一底层错误值或指针,不支持字符串匹配;须用%w包裹、预定义变量或自定义Unwrap方法才能正确识别。
因为 errors.Is 只检查错误链中是否存在某个**底层错误值(value)或其指针**,不支持字符串匹配、模糊比较或自定义逻辑。如果你用 errors.New("timeout") 创建的错误和另一个同内容的 errors.New("timeout") 比较,errors.Is 一定返回 false —— 它们是两个不同地址的指针。
只有当错误被显式地用 fmt.Errorf("...: %w", err) 或 errors.Join 等方式包裹(即包含 %w 动词),它才进入错误链;否则 errors.Is 查不到。
%w 包裹底层错误%s 或拼接字符串,如 fmt.Errorf("failed: %s", err.Error())
errors.Unwrap 只能解一层,而 errors.Is 会自动遍历整个链(包括嵌套的 %w)err := io.EOF
wrapped := fmt.Errorf("read failed: %w", err)
fmt.Println(errors.Is(wrapped, io.EOF)) // true
要实现 Unwrap 方法如果自己定义了错误结构体(比如 type MyError struct{ Msg string; Code int }),默认 errors.Is 查不到它是否等于某个值,除非你显式实现 Unwrap() error 并返回底层错误。
Unwrap 应返回 nil
io.ReadFull),就返回那个错误Unwrap 中返回新构造的 errors.New —— 地址不同,errors.Is 仍失败type MyError struct {
Err error
}
func (e *MyError) Error() string { return "my error" }
func (e *MyError) Unwrap() error { return e.Err } // ✅ 这样 errors.Is 才能穿透
Go 标准库导出的错误(如 io.EOF、os.ErrNotExist)是包级变量,地址唯一。直接拿它们做 errors.Is(err, io.EOF) 是安全且推荐的;但千万别写成 errors.Is(err, errors.New("EOF"))。
立即学习“go语言免费学习笔记(深入)”;
errors.Is(err, io.EOF)
errors.Is(err, os.ErrNotExist)
errors.Is(err, errors.New("file does not exist"))
Unwrap 或提供了 IsXXX() 辅助函数%w,导致上游再也无法用 errors.Is 判断原始错误类型。这个断点一旦出现,调试时就会发现“明明报了 EOF,却进不了 EOF 分支”。