error是什么
error是golang的错误处理机制,error接口定义如下:
type error interface {
Error() string
}
创建error
创建方式有两种:
-
errors.New()
-
fmt.Errorf()
errors.New
示例:
func main() {
err := errors.New("这是一个error")
fmt.Printf("type:%T val:%v\n", err, err) // 输出结果:type:*errors.errorString val:这是一个error
}
errors.New
会创建errorString
结构体实例,返回其指针,源码如下:
// New returns an error that formats as the given text.
// Each call to New returns a distinct error value even if the text is identical.
func New(text string) error {
return &errorString{text}
}
// errorString is a trivial implementation of error.
type errorString struct {
s string
}
func (e *errorString) Error() string {
return e.s
}
fmt.Errorf
示例:
func main() {
err1 := errors.New("这是一个error")
fmt.Println("err1:", err1) // 输出结果:err1: 这是一个error
err2 := fmt.Errorf("这是第%d个error", 2)
fmt.Printf("err2Type:%T err2Val:%v\n", err2, err2) // 输出结果:err2Type:*errors.errorString err2Val:这是第2个error
err3 := fmt.Errorf("这是第%d个error,嵌套err:%w", 3, err1)
fmt.Printf("err3Type:%T err3Val:%v\n", err3, err3) // 输出结果:err3Type:*fmt.wrapError err3Val:这是第3个error,嵌套err:这是一个error
err4 := fmt.Errorf("这是第%d个error,嵌套err1:%w,嵌套err2:%w", 4, err1, err2)
fmt.Printf("err4Type:%T err4Val:%v\n", err4, err4) // 输出结果:err4Type:*errors.errorString err4Val:这是第4个error,嵌套err1:这是一个error,嵌套err2:%!w(*errors.errorString=&{这是第2个error})
}
可以看到fmt.Errorf()
可以返回两种error类型:
*errors.errorString
*fmt.wrapError
具体源码:
源码解读可以看
fmt
格式化输出源码,是一模一样的
// Errorf formats according to a format specifier and returns the string as a
// value that satisfies error.
//
// If the format specifier includes a %w verb with an error operand,
// the returned error will implement an Unwrap method returning the operand. It is
// invalid to include more than one %w verb or to supply it with an operand
// that does not implement the error interface. The %w verb is otherwise
// a synonym for %v.
func Errorf(format string, a ...any) error {
p := newPrinter()
p.wrapErrs = true
p.doPrintf(format, a)
s := string(p.buf)
var err error
if p.wrappedErr == nil {
err = errors.New(s)
} else {
err = &wrapError{s, p.wrappedErr}
}
p.free()
return err
}
fmt.wrapError类型
:
type wrapError struct {
msg string
err error
}
// 实现了error接口
func (e *wrapError) Error() string {
return e.msg
}
// 实现了xerrors.Wrapper接口
func (e *wrapError) Unwrap() error {
return e.err
}
xerrors.Wrapper
接口:
// A Wrapper provides context around another error.
type Wrapper interface {
// Unwrap returns the next error in the error chain.
// If there is no next error, Unwrap returns nil.
Unwrap() error
}
注释中的关键字:the error chain,说明error是一个链式模型(链表) ,这也是实际项目中使用error的常见形式
链式模型—error chain
xerrors.Wrapper
接口定义:
// A Wrapper provides context around another error.
type Wrapper interface {
// Unwrap returns the next error in the error chain.
// If there is no next error, Unwrap returns nil.
Unwrap() error
}
什么是链式模型?什么是 the error chain
?
简单理解就是:嵌套、套娃,一个error嵌套另一个error,一直嵌套下去...
示例:
func main() {
err1 := errors.New("这是一个error")
err2 := fmt.Errorf("这是第%d个error,嵌套err1:%w", 2, err1)
err3 := fmt.Errorf("这是第%d个error,嵌套err2:%w", 3, err2)
err4 := fmt.Errorf("这是第%d个error,嵌套err3:%w", 4, err3)
fmt.Println(err4)
unwrapped1 := errors.Unwrap(err4)
fmt.Println(unwrapped1)
unwrapped2 := errors.Unwrap(unwrapped1)
fmt.Println(unwrapped2)
unwrapped3 := errors.Unwrap(unwrapped2)
fmt.Println(unwrapped3)
unwrapped4 := errors.Unwrap(unwrapped3)
fmt.Println(unwrapped4)
}
输出结果:
这是第4个error,嵌套err3:这是第3个error,嵌套err2:这是第2个error,嵌套err1:这是一个error
这是第3个error,嵌套err2:这是第2个error,嵌套err1:这是一个error
这是第2个error,嵌套err1:这是一个error
这是一个error
<nil>
errors.Unwrap
方法源码
// Unwrap returns the result of calling the Unwrap method on err, if err's
// type contains an Unwrap method returning error.
// Otherwise, Unwrap returns nil.
func Unwrap(err error) error {
u, ok := err.(interface {
Unwrap() error
})
if !ok {
return nil
}
return u.Unwrap()
}
比较error——errors.Is()
示例:
func main() {
err1 := errors.New("这是一个error")
err2 := fmt.Errorf("这是第%d个error,嵌套err1:%w", 2, err1)
err3 := fmt.Errorf("这是第%d个error,嵌套err2:%w", 3, err2)
err4 := fmt.Errorf("这是第%d个error,嵌套err3:%w", 4, err3)
fmt.Println(err4)
unwrapped1 := errors.Unwrap(err4)
fmt.Println(unwrapped1)
unwrapped2 := errors.Unwrap(unwrapped1)
fmt.Println(unwrapped2)
unwrapped3 := errors.Unwrap(unwrapped2)
fmt.Println(unwrapped3)
unwrapped4 := errors.Unwrap(unwrapped3)
fmt.Println(unwrapped4)
ok1 := errors.Is(err4, err3)
ok2 := errors.Is(err4, err2)
ok3 := errors.Is(err4, err1)
ok4 := errors.Is(err4, nil)
ok5 := errors.Is(err4, errors.New("这是一个error"))
fmt.Println(ok1, ok2, ok3, ok4, ok5) // 输出:true true true false false
}
输出结果:
这是第4个error,嵌套err3:这是第3个error,嵌套err2:这是第2个error,嵌套err1:这是一个error
这是第3个error,嵌套err2:这是第2个error,嵌套err1:这是一个error
这是第2个error,嵌套err1:这是一个error
这是一个error
<nil>
true true true false false
errors.Is()
源码
重点:reports whether any error in err's chain matches target. 会判断入参err的error链中是否有error与入参target匹配。
// Is reports whether any error in err's chain matches target.
//
// The chain consists of err itself followed by the sequence of errors obtained by
// repeatedly calling Unwrap.
//
// An error is considered to match a target if it is equal to that target or if
// it implements a method Is(error) bool such that Is(target) returns true.
//
// An error type might provide an Is method so it can be treated as equivalent
// to an existing error. For example, if MyError defines
//
// func (m MyError) Is(target error) bool { return target == fs.ErrExist }
//
// then Is(MyError{}, fs.ErrExist) returns true. See syscall.Errno.Is for
// an example in the standard library. An Is method should only shallowly
// compare err and the target and not call Unwrap on either.
func Is(err, target error) bool {
if target == nil {
return err == target
}
isComparable := reflectlite.TypeOf(target).Comparable()
for {
if isComparable && err == target {
return true
}
if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) {
return true
}
// TODO: consider supporting target.Is(err). This would allow
// user-definable predicates, but also may allow for coping with sloppy
// APIs, thereby making it easier to get away with them.
if err = Unwrap(err); err == nil {
return false
}
}
}
核心:
reflectlite.TypeOf(target).
Comparable
()
,判断类型是否可比较,指针是可比较的。if x, ok := err.(
interface{ Is(error) bool }
); ok &&
x.Is(target)
{ return true }
,判断是否实现了interface{ Is(error) bool })
这个接口,然后调用Is()
方法比较。if
err = Unwrap(err)
; err == nil { return false }
,继续判断err的error链中是否有error与target匹配。
项目中常见用法
需求
有时候想要针对特定error进行特殊处理,而不是遇到error就抛出。
关键:要能比较error是否是指定error。
一般用法
1 事先创建好指定error
做法:
- 首先,定义好全局error
- 然后,使用
==
或errors.Is
来判断error是否是指定error,是则执行指定处理逻辑
示例
var (
ErrorCase1 = errors.New("error case 1")
ErrorCase2 = errors.New("error case 2")
ErrorCase3 = errors.New("error case 3")
)
func main() {
err := DoSomething()
fmt.Println("raw err:", err)
// 实际是比较指针
if err == ErrorCase1 {
fmt.Println("case1 process...")
return
}
// 也可用errors.Is方法判断
if errors.Is(err, ErrorCase2) {
fmt.Println("case2 process...")
return
}
if err == ErrorCase3 {
fmt.Println("case3 process...")
return
}
}
func DoSomething() error {
fmt.Println("do something...")
rand.Seed(time.Now().UnixMilli())
i := rand.Int()
if i%3 == 0 {
return ErrorCase1
} else if i%3 == 1 {
return ErrorCase2
} else {
return ErrorCase3
}
}
gorm框架举例
gorm框架事先定义好的error举例:
// ErrRecordNotFound record not found error
var ErrRecordNotFound = errors.New("record not found")
var (
// ErrRecordNotFound record not found error
ErrRecordNotFound = logger.ErrRecordNotFound
// ErrInvalidTransaction invalid transaction when you are trying to `Commit` or `Rollback`
ErrInvalidTransaction = errors.New("invalid transaction")
// ErrNotImplemented not implemented
ErrNotImplemented = errors.New("not implemented")
// ErrMissingWhereClause missing where clause
ErrMissingWhereClause = errors.New("WHERE conditions required")
// ErrUnsupportedRelation unsupported relations
ErrUnsupportedRelation = errors.New("unsupported relations")
// ErrPrimaryKeyRequired primary keys required
ErrPrimaryKeyRequired = errors.New("primary key required")
......
)
2 自定义error,使用code唯一标识error
- 首先,自定义error:
// 继承、扩展error接口
type IError interface {
error
GetCode() int
GetMsg() string
}
// 实现IError接口
type BizError struct {
Code int
Msg string
}
func NewBizError(Code int, Msg string) *BizError {
e := &BizError{
Code: Code,
Msg: Msg,
}
return e
}
func (b *BizError) Error() string {
return b.Msg
}
func (b *BizError) GetCode() int {
return b.Code
}
func (b *BizError) GetMsg() string {
return b.Msg
}
// Is 判断error是否相等
func (b *BizError) Is(err error) bool {
err0, ok := err.(*BizError)
if ok {
return err0.GetCode() == b.GetCode()
}
return false
}
- 然后,定义全局error唯一标识,用来表明特定场景的error:
const (
ErrorCode1 = 1001
ErrorCode2 = 1002
ErrorCode3 = 1003
)
- 然后,使用:
func main() {
err := DoSomething()
fmt.Println("raw err:", err)
if err == nil {
fmt.Println("no err")
return
}
ie, ok := err.(IError)
if !ok {
fmt.Println("not bizError. process system error...")
return
}
if ie.GetCode() == ErrorCode1 {
fmt.Println("error case1 process...")
return
}
if ie.GetCode() == ErrorCode2 {
fmt.Println("error case2 process...")
return
}
if ie.GetCode() == ErrorCode3 {
fmt.Println("error case3 process...")
return
}
}
func DoSomething() error {
fmt.Println("do something...")
rand.Seed(time.Now().UnixMilli())
i := rand.Int()
if i%4 == 0 {
return errors.New("system error")
}
if i%4 == 1 {
return NewBizError(ErrorCode1, "error case 1")
}
if i%4 == 2 {
return NewBizError(ErrorCode2, "error case 2")
}
if i%4 == 3 {
return NewBizError(ErrorCode3, "error case 3")
}
return nil
}