在Go语言的标准库中,errors包和fmt包都提供了创建错误的函数,其中errors.New和fmt.Errorf是最常用的两个错误创建方式,两者在功能和使用场景上存在明显差异,理解这些差异能帮助开发者更合理地处理程序中的错误。

基础功能对比
errors.New是errors包提供的函数,用于创建一个简单的静态错误,错误内容就是传入的字符串,不支持格式化操作。fmt.Errorf是fmt包提供的函数,支持类似fmt.Sprintf的格式化能力,可以动态拼接错误内容,同时还支持Go 1.13引入的错误包装功能。
两者的基础使用示例如下:
package main
import (
"errors"
"fmt"
)
func main() {
// 使用errors.New创建错误
err1 := errors.New("连接数据库失败")
fmt.Println(err1) // 输出:连接数据库失败
// 使用fmt.Errorf创建错误
dbAddr := "127.0.0.1:3306"
err2 := fmt.Errorf("连接数据库%s失败", dbAddr)
fmt.Println(err2) // 输出:连接数据库127.0.0.1:3306失败
}
核心差异点
1. 是否支持格式化
errors.New仅能接收固定的字符串作为参数,无法动态拼接内容,适合错误内容完全固定的场景。fmt.Errorf支持格式化占位符,比如%s、%d、%w等,可以动态将变量内容拼接到错误信息中,适合错误内容需要包含动态参数的场景。
2. 错误包装能力
errors.New创建的错误是独立的错误实例,不包含其他错误的上下文信息。fmt.Errorf在使用%w占位符时,可以将一个错误包装到新创建的错误中,形成错误链,后续可以通过errors.Is和errors.As判断底层错误类型。
错误包装的示例如下:
package main
import (
"errors"
"fmt"
"os"
)
func readFile() error {
_, err := os.Open("test.txt")
if err != nil {
// 使用%w包装原始错误
return fmt.Errorf("读取文件失败: %w", err)
}
return nil
}
func main() {
err := readFile()
if err != nil {
// 判断底层错误是否为文件不存在错误
if errors.Is(err, os.ErrNotExist) {
fmt.Println("文件不存在")
}
}
}
3. 错误实例的唯一性
每次调用errors.New都会创建一个新的错误实例,即使传入的字符串完全相同,两个错误也不相等。而fmt.Errorf如果不使用%w包装,创建的错误实例同样是独立的,但是它的核心差异还是在于格式化能力。
错误实例对比示例:
package main
import (
"errors"
"fmt"
)
func main() {
err1 := errors.New("参数错误")
err2 := errors.New("参数错误")
fmt.Println(err1 == err2) // 输出:false
err3 := fmt.Errorf("参数错误")
err4 := fmt.Errorf("参数错误")
fmt.Println(err3 == err4) // 输出:false
}
使用场景选择
- 当错误内容是固定字符串,不需要包含动态参数,也不需要包装其他错误时,优先使用
errors.New,比如定义包级别的导出错误变量:var ErrInvalidParam = errors.New("无效的参数") - 当错误内容需要包含动态参数,比如变量值、地址、ID等信息时,使用
fmt.Errorf配合对应的格式化占位符拼接内容。 - 当需要保留底层错误的上下文,方便上层调用者判断原始错误类型时,使用
fmt.Errorf配合%w占位符包装错误。
底层实现简单说明
errors.New的实现非常简单,就是创建一个包含传入字符串的errorString结构体实例,该结构体实现了error接口的Error()方法,返回对应的字符串。
fmt.Errorf的实现则复杂很多,它会先解析格式化字符串,处理各个占位符,当遇到%w占位符时,会将对应的错误保存到新创建的错误实例中,形成错误链,支持后续的errors.Is和errors.As查询。
| 对比维度 | errors.New | fmt.Errorf |
|---|---|---|
| 格式化支持 | 不支持 | 支持 |
| 错误包装 | 不支持 | 支持(使用%w时) |
| 适用场景 | 固定内容错误、包级别错误定义 | 动态内容错误、需要包装上下文的错误 |
在实际开发中,不需要刻意区分两者的使用,根据需求选择即可,固定内容用errors.New,需要动态拼接或者包装错误用fmt.Errorf,这样能让错误处理更清晰合理。
errors.Newfmt.ErrorfGo错误创建错误包装修改时间:2026-07-14 12:39:28