Golang中fmt.Println()无法打印包装的自定义错误
Golang中fmt.Println()无法打印包装的自定义错误
我有一个自定义错误类型,并以典型方式实现了 error 接口和 Unwrap() 方法。现在假设我有一个三层深的函数调用栈。所有层级都会添加一些错误。
第一层(最深)通过以下方式包装某个 io 错误:
fmt.Errorf("err occured in level1 %w", ioErr)
第二层将上面返回的第一层错误包装到我的自定义错误中:
return NewCustomError("err occurred in level2", lvl1Err)
第三层再次将上面返回的自定义错误包装到一个“普通”的 Go 错误中,通过:
fmt.Errorf("err occured in level3 %w", customErr)
在 main() 中,我现在对第三层的错误执行 fmt.Println(err)。但打印出来的链只显示了第三层错误和第二层错误。一旦 Println() “碰到”我的自定义错误,它就停止了“打印链”。
我做了以下尝试:
- 如果我在第二层用
fmt.Errorf()调用替换我的自定义错误,main 中的fmt.Println()会打印所有层级的错误。 - 如果我在
main()中暂停调试器,我可以清楚地看到,即使使用了自定义错误,链中所有错误也都存在。 - 如果我使用 Go 的
errors.Unwrap(),我可以手动解包并打印所有错误,包括我的自定义错误。
简而言之:每个错误都正确地链接在一起,但不知何故,fmt.Println(err) 在到达自定义错误时停止了包装打印。
我一直以为 fmt.Println() 会做与上面第3点中我做的相同的事情:通过调用 Unwrap() 并打印错误,直到它为 nil,来遍历所有错误。然而,它在到达自定义错误时却停止了。我想知道这是为什么?
你可以在这里找到一个简单的示例代码,以便更好地理解:
非常感谢任何帮助, 祝好
更多关于Golang中fmt.Println()无法打印包装的自定义错误的实战教程也可以访问 https://www.itying.com/category-94-b0.html
你好,
感谢你的回答!你提到的那行代码不是问题所在。但在你的示例代码中,第15行的 return e.Msg + e.Err.Error() 代码是关键点。顺便说一句:为了遵循 Go 标准库使用的相同模式,它应该是 return e.Msg + „: „ + e.Err.Error()。
我在 StackOverflow 上提了一个问题,它比我上面在这里的帖子更好地阐述了我的观点,并且它包含了一个比我在这里做的更简单、更短的示例(对此表示抱歉):
我仍然是一个 Go 语言新手,所以如果我表述不清,我表示歉意。但感谢你的回复,playground 代码的第15行确实切中了要害。
更多关于Golang中fmt.Println()无法打印包装的自定义错误的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
是否是因为这行代码将实际的错误设置为了 nil?
func parse(fileContent string) (string, error) {
// 假设我们在这里处理给定的文件内容并进行一些解析。
// 但我们的解析失败了。现在我们必须“抛出”我们自己的自定义错误。
// 因为在顶层的 main() 函数中,我们想要区分不同的错误。
// ...
// 在这里做一些花哨的解析工作。
// ...
// 现在解析在这里失败("WTF?"),我们返回自己的自定义错误:
if sim.SimulateErrorInPackage2 {
return "", &app.CustomError{Err: nil, Msg: "parsing failed cause of WTF situation"}
}
// 但如果一切顺利,返回解析后的参数(当然我们伪造了这个):
parsedParams := "/C echo Hello World (some pseudo result from successful parsing in package2)"
return parsedParams, nil
}
也许,这个链接能提供更多见解;

在Go语言中,fmt.Println() 打印错误链时确实会通过 Unwrap() 方法遍历错误,但自定义错误类型需要正确实现 error 接口和 Unwrap() 方法才能被 fmt 包识别。根据你的描述,问题可能出现在自定义错误的 Error() 方法实现上。fmt 包在打印错误链时,会检查每个错误是否实现了 Unwrap() error 方法,但自定义错误的 Error() 方法返回的字符串可能没有包含完整的错误链信息。
以下是一个示例,展示如何正确实现自定义错误,确保 fmt.Println() 能打印整个错误链:
package main
import (
"errors"
"fmt"
"io"
)
// 自定义错误类型
type CustomError struct {
msg string
err error
}
// 实现 error 接口的 Error() 方法
func (e *CustomError) Error() string {
return e.msg
}
// 实现 Unwrap() 方法以支持错误链
func (e *CustomError) Unwrap() error {
return e.err
}
// 创建自定义错误的函数
func NewCustomError(msg string, err error) error {
return &CustomError{msg: msg, err: err}
}
func level1() error {
// 模拟一个底层错误
ioErr := io.EOF
return fmt.Errorf("err occurred in level1: %w", ioErr)
}
func level2() error {
lvl1Err := level1()
// 使用自定义错误包装第一层错误
return NewCustomError("err occurred in level2", lvl1Err)
}
func level3() error {
customErr := level2()
// 使用 fmt.Errorf 包装自定义错误
return fmt.Errorf("err occurred in level3: %w", customErr)
}
func main() {
err := level3()
fmt.Println(err)
}
运行上述代码,输出将是完整的错误链:
err occurred in level3: err occurred in level2: err occurred in level1: EOF
如果 fmt.Println() 仍然无法打印完整链,请检查自定义错误的 Error() 方法是否返回了正确的字符串。fmt 包在打印错误链时,会递归调用每个错误的 Error() 方法并拼接它们。确保自定义错误的 Error() 方法返回的字符串包含了内部错误的信息,例如:
func (e *CustomError) Error() string {
if e.err != nil {
return fmt.Sprintf("%s: %v", e.msg, e.err)
}
return e.msg
}
这样修改后,fmt.Println() 应该能正确打印整个错误链。

