errors.New适用于固定无变量错误,fmt.Errorf支持格式化和%w保留错误链;errors.Is检查特定错误值,errors.As提取具体错误类型;自定义错误需用结构体并实现Unwrap;errors.Unwrap仅解一层且有panic风险。

Go 中 errors.New 和 fmt.Errorf 的适用场景区别
直接用 errors.New 创建的错误是无格式、无上下文的静态字符串;而 fmt.Errorf 支持格式化,尤其适合拼接动态信息(如 ID、状态码)。但注意:若只做简单字符串包装,fmt.Errorf("something: %w", err) 中的 %w 才能保留原始错误链,纯 %s 会切断。
常见误用:fmt.Errorf("failed to open file: %s", err) —— 这样原始错误类型和堆栈(如有)全丢了。应改用 %w 并确保传入的是 error 类型值。
-
errors.New("invalid input"):适合固定、无变量的错误(如参数校验失败) -
fmt.Errorf("timeout after %dms", timeoutMs):需要嵌入数值或字符串时用 -
fmt.Errorf("read header failed: %w", io.ErrUnexpectedEOF):需保留底层错误时必须用%w
如何正确使用 errors.Is 和 errors.As 检查错误
errors.Is 判断是否为某个**特定错误值**(比如全局变量 ErrNotFound),而 errors.As 用于提取错误链中**某个具体类型的错误实例**(比如想拿到 *os.PathError 来访问 Path 字段)。
容易踩的坑:对自定义错误类型,必须实现 Unwrap() error 方法才能被 errors.Is/errors.As 向下遍历;否则只检查最外层错误。
立即学习“go语言免费学习笔记(深入)”;
var ErrNotFound = errors.New("not found")
func doSomething() error {
return fmt.Errorf("in service: %w", ErrNotFound)
}
err := doSomething()
fmt.Println(errors.Is(err, ErrNotFound)) // true
fmt.Println(errors.Is(err, errors.New("not found"))) // false —— 不同实例
自定义错误类型时为什么推荐嵌入 struct{} 而非字符串
字符串错误(如 errors.New("db timeout"))无法携带额外字段,也不支持行为扩展。一旦需要加字段(如 Code、Retryable)、方法(如 LogMessage())或实现 Unwrap(),就必须用结构体。
标准做法是定义一个不可导出字段(如 err)并嵌入,再提供构造函数:
type MyError struct {
Code int
Message string
err error // 用于 Unwrap
}
func (e *MyError) Error() string { return e.Message }
func (e *MyError) Unwrap() error { return e.err }
func NewMyError(code int, msg string, cause error) *MyError {
return &MyError{Code: code, Message: msg, err: cause}
}
这样既能用 errors.As(err, &e) 提取,也能用 errors.Is(err, someKnownErr) 匹配(只要 someKnownErr 是同一类型且 Unwrap() 返回匹配值)。
errors.Unwrap 的实际用途与调用风险
errors.Unwrap 只返回错误链中**下一个错误**(即 Unwrap() 方法返回的值),不是“展开全部”。它常用于手动遍历错误链,但绝大多数情况应优先用 errors.Is 或 errors.As —— 它们内部已处理多层嵌套。
风险点:多次调用 errors.Unwrap 可能 panic(如果某层返回 nil),且无法判断是否到底层。所以不建议裸写循环解包,除非你明确知道错误链深度和结构。
- 安全写法:
for err != nil { ... err = errors.Unwrap(err) },但要配合errors.Is做终止判断 - 典型误用:
errors.Unwrap(errors.Unwrap(err))—— 第二层可能为nil,导致 panic - 真正需要手动解包的场景极少,比如日志系统里要逐层打印错误消息
%w 是唯一能形成可遍历链的操作符,漏写或错用会直接让 Is/As 失效。










