在Golang的标准库中,error是内置的错误接口,很多场景下我们需要判断错误的具体类型,或者将通用error转换为自定义的错误类型,这时候就需要用到错误类型转换的相关方法。

errors.As的基本用法
errors.As是Go 1.13版本引入的方法,它的作用是判断错误链中是否存在某个特定类型的错误,并且将该错误赋值给目标变量。它的函数签名如下:
func As(err error, target interface{}) bool
使用errors.As需要满足两个条件:target必须是一个非nil的指针,并且指针指向的类型要实现了error接口。下面是一个简单的使用示例:
package main
import (
"errors"
"fmt"
)
// 自定义错误类型
type MyError struct {
Msg string
}
// 实现error接口
func (e *MyError) Error() string {
return e.Msg
}
func main() {
// 创建一个自定义错误
var err error = &MyError{Msg: "自定义错误内容"}
var target *MyError
// 使用errors.As判断错误类型并转换
if errors.As(err, &target) {
fmt.Println("转换成功,错误信息:", target.Msg)
} else {
fmt.Println("转换失败")
}
}
errors.As和类型断言的区别
很多开发者会疑惑,为什么不用类型断言来做错误类型转换,其实两者的适用场景不同:
- 类型断言只能判断当前error是否是指定类型,无法处理错误链中的错误。如果错误被多次包装,类型断言就会失效。
- errors.As会沿着错误链依次判断每个错误,只要链中存在指定类型的错误就会返回true,更适合包装错误的场景。
下面的例子展示了类型断言在包装错误场景下的失效问题:
package main
import (
"errors"
"fmt"
)
type MyError struct {
Msg string
}
func (e *MyError) Error() string {
return e.Msg
}
func main() {
originalErr := &MyError{Msg: "原始错误"}
// 对错误进行包装
wrappedErr := fmt.Errorf("包装错误: %w", originalErr)
// 使用类型断言判断,会失败
if _, ok := wrappedErr.(*MyError); ok {
fmt.Println("类型断言成功")
} else {
fmt.Println("类型断言失败")
}
// 使用errors.As判断,会成功
var target *MyError
if errors.As(wrappedErr, &target) {
fmt.Println("errors.As转换成功,错误信息:", target.Msg)
}
}
Golang其他错误类型转换方法汇总
1. 类型断言
适用于没有错误包装的场景,直接判断当前error的类型,用法如下:
func main() {
var err error = &MyError{Msg: "测试错误"}
if e, ok := err.(*MyError); ok {
fmt.Println("类型断言转换成功:", e.Msg)
}
}
2. errors.Is判断错误值
如果我们需要判断错误是否是某个特定的错误值,可以使用errors.Is,它也会沿着错误链查找,和errors.As的区别是它判断的是错误值是否相等,而不是类型:
package main
import (
"errors"
"fmt"
)
var ErrNotFound = errors.New("资源不存在")
func main() {
err := fmt.Errorf("查询失败: %w", ErrNotFound)
if errors.Is(err, ErrNotFound) {
fmt.Println("错误是资源不存在错误")
}
}
3. 自定义错误判断方法
如果自定义错误类型有特定的判断方法,也可以直接在错误链中遍历判断,不过这种方式不如errors.As通用:
package main
import (
"errors"
"fmt"
)
type MyError struct {
Code int
Msg string
}
func (e *MyError) Error() string {
return e.Msg
}
func (e *MyError) IsNotFound() bool {
return e.Code == 404
}
func main() {
err := &MyError{Code: 404, Msg: "资源不存在"}
var target *MyError
// 自定义类型判断
if errors.As(err, &target) && target.IsNotFound() {
fmt.Println("是404错误")
}
}
使用注意事项
- 使用errors.As时,target必须是指向错误类型的指针,否则会panic。
- 自定义错误类型如果要支持errors.As,需要实现error接口,也就是有Error() string方法。
- 如果错误链过长,errors.As会依次遍历,性能上会有少量损耗,一般场景下可以忽略。