errors.As 能安全遍历错误链并提取指定类型错误,解决类型断言无法处理包装错误的问题,适用于需访问自定义错误字段的场景。

errors.As
fmt.Errorf
%w
在 Go 1.13 之后,
errors.As
Unwrap()
true
false
它的函数签名是
func As(err error, target any) bool
err
target
*MyCustomError
target
&myCustomErrorVar
As
target
我们来看一个具体的例子。假设我们有一个自定义的错误类型,它可能包含一些额外的上下文信息:
立即学习“go语言免费学习笔记(深入)”;
package main
import (
"errors"
"fmt"
)
// MyCustomError 定义一个自定义错误类型,包含一个错误码
type MyCustomError struct {
Code int
Message string
inner error // 用于包装内部错误
}
// Error 方法实现了 error 接口
func (e *MyCustomError) Error() string {
if e.inner != nil {
return fmt.Sprintf("custom error %d: %s (wrapped: %v)", e.Code, e.Message, e.inner)
}
return fmt.Sprintf("custom error %d: %s", e.Code, e.Message)
}
// Unwrap 方法允许 errors.As 和 errors.Is 遍历错误链
func (e *MyCustomError) Unwrap() error {
return e.inner
}
// SimulateOperation 模拟一个可能返回自定义错误的函数
func SimulateOperation(shouldFail bool) error {
if shouldFail {
// 包装一个标准库错误
return &MyCustomError{
Code: 1001,
Message: "数据处理失败",
inner: fmt.Errorf("原始数据库错误: %w", errors.New("record not found")),
}
}
return nil
}
func main() {
// 场景一:操作失败,返回自定义错误
err := SimulateOperation(true)
if err != nil {
var customErr *MyCustomError // 声明一个指向 MyCustomError 类型的指针
if errors.As(err, &customErr) {
fmt.Printf("通过 errors.As 成功捕获到自定义错误:Code=%d, Message='%s'\n", customErr.Code, customErr.Message)
// 此时 customErr 变量已经包含了 MyCustomError 的值
// 我们可以进一步检查内部错误,例如使用 errors.Is
if errors.Is(customErr.Unwrap(), errors.New("record not found")) {
fmt.Println("自定义错误内部包含 'record not found' 错误。")
}
} else {
fmt.Printf("捕获到其他错误:%v\n", err)
}
}
fmt.Println("---")
// 场景二:操作成功
err = SimulateOperation(false)
if err != nil {
var customErr *MyCustomError
if errors.As(err, &customErr) {
fmt.Printf("通过 errors.As 成功捕获到自定义错误:Code=%d, Message='%s'\n", customErr.Code, customErr.Message)
} else {
fmt.Printf("捕获到其他错误:%v\n", err)
}
} else {
fmt.Println("操作成功,没有错误。")
}
fmt.Println("---")
// 场景三:包装了一个不同类型的错误,看看 errors.As 如何处理
anotherErr := fmt.Errorf("外部服务调用失败: %w", errors.New("timeout"))
var customErr *MyCustomError
if errors.As(anotherErr, &customErr) {
fmt.Printf("意外捕获到自定义错误:%v\n", customErr)
} else {
fmt.Printf("anotherErr 不是 MyCustomError 类型,或者不包含 MyCustomError 类型:%v\n", anotherErr)
}
}在这个例子中,
errors.As(err, &customErr)
err
*MyCustomError
SimulateOperation(true)
*MyCustomError
errors.As
customErr
true
customErr
Code
Message
err.(MyCustomError)
这是一个非常好的问题,也是 Go 错误处理演进中一个重要的里程碑。在 Go 1.13 之前,或者说在
errors.As
if _, ok := err.(MyCustomError); ok {}想象一下,如果你的错误被包装了,比如
fmt.Errorf("操作失败: %w", &MyCustomError{...})err
*fmt.wrapError
*MyCustomError
err.(*MyCustomError)
err
MyCustomError
我个人觉得,这正是
errors.As
errors.As
errors.Is
这又是 Go 错误处理中一对经常被混淆但又至关重要的函数。简单来说,它们解决的是不同的问题:
errors.Is
io.EOF
os.ErrNotExist
var ErrNotFound = errors.New("not found")errors.Is
os.ErrNotExist
io.EOF
if errors.Is(err, os.ErrNotExist) {
fmt.Println("文件不存在,需要创建。")
}errors.As
errors.As
var netErr *net.OpError
if errors.As(err, &netErr) {
fmt.Printf("这是一个网络操作错误,操作类型: %s, 地址: %s\n", netErr.Op, netErr.Addr)
// 进一步检查 netErr.Err 可能是 io.EOF 或 syscall.ECONNREFUSED
}可以这样理解:
errors.Is
errors.As
在 Go 中设计和使用自定义错误类型,是构建健壮应用的关键。这里有一些我个人总结的实践建议:
明确何时使用值,何时使用类型:
var ErrFoo = errors.New("foo")errors.Is
Temporary()
Timeout()
errors.As
实现 Unwrap()
Unwrap() error
errors.As
errors.Is
As
Is
type MyWrappedError struct {
Msg string
Cause error // 内部包装的错误
}
func (e *MyWrappedError) Error() string {
return fmt.Sprintf("%s: %v", e.Msg, e.Cause)
}
func (e *MyWrappedError) Unwrap() error {
return e.Cause // 返回被包装的错误
}考虑实现特定接口: Go 的错误处理哲学鼓励通过接口来定义错误行为。例如,
net
net.Error
Timeout()
Temporary()
type MyNetworkError struct {
// ...
}
func (e *MyNetworkError) Timeout() bool { return true }
func (e *MyNetworkError) Temporary() bool { return true }
// ...这样,即使你的错误类型不同,只要实现了相同的接口,就可以用统一的方式处理。
避免过度包装和过于复杂的错误结构: 虽然错误链很有用,但也要避免为了包装而包装。有时候,一个简单的
fmt.Errorf("failed to process X: %w", err)错误信息要清晰且对用户友好:
Error()
Error()
错误码的运用: 对于复杂的系统,错误码是一种常见的模式,它能提供结构化的错误信息,便于机器解析和国际化。将错误码作为自定义错误类型的一个字段,然后通过
errors.As
通过遵循这些实践,你将能够构建出更健壮、更易于维护和调试的 Go 应用程序。
errors.As
以上就是Golang中errors.As函数如何安全地将error转换为具体错误类型的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号