You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
// simple string-based errorerr1:=errors.New("math: square root of negative number")
// with formattingerr2:=fmt.Errorf("math: square root of negative number %g", x)
funcbar() error {
err:=foo()
returnfmt.Errorf("some more info on err, the source err is :%w", err)
}
%v(value) 则只是将原错误信息的文本形成新文本,而不会有包装操作。
对应地,通过 errors.Unwrap 可解出被封装的原始错误信息:
funcmain() {
err:=bar()
iferr!=nil {
fmt.Println(err)
ifsourceErr:=errors.Unwrap(err); sourceErr!=nil {
fmt.Println(sourceErr)
}
}
}
// some more info on err, the source err is :status error with user id 1000// status error with user id 1000
自定义错误要支持 Unsrap 需要实现该方法:
type StatusError struct {
Status Status
Message string
+ err error
}
+ func (se StatusError) Unwrap()error{+ return se.err+ }
[golang] 错误处理
字符串类型的错误
自定义错误
自定义错误可携带额外信息,比如错误码等。
先定义一个错误码枚举:
然后使用上面的枚举来实现自定义错误:
测试:
上面示例代码中,注意两点:
error
,这样调用方可以不依赖具体的错误类型,更加解耦哨兵错误
有一种错误,Sentinel 错误,约定以
Err
开头,定义在包作用域内,当调用方接收到该类型错误后,表示程序无需继续往下,应该终止执行。比如标准库中解压时文件类型不对的错误
ErrorFormat
:查看标准库中的实现,可看到其就是普通的
error
类型,只是命名按照了上述约定:Error 的包装
接收到 error 后,可添加点信息在上面,再将其返回出去。这样可形成一条错误链。
前面涉及到的
fmt.Errorf
支持一个%w
(wrap)参数,可接入一个error
类型从而在其上面进行二次包装。%v
(value) 则只是将原错误信息的文本形成新文本,而不会有包装操作。对应地,通过
errors.Unwrap
可解出被封装的原始错误信息:自定义错误要支持
Unsrap
需要实现该方法:Is
和As
既然错误可以二次包装,那么问题来了,如果对哨兵错误进行了包装,调用方如何知道错误中包含了哨兵错误呢。Go 中提供了两个方法
[errors.Is](http://errors.Is)
和[errors.As](http://errors.As)
来解决这个问题。内部实现上,
errors.Is
是通过不断Unwrap
错误并与之进行对比来判断的,所以,自定义错误要支持对比,需要实现Is
方法:As
判断一个错误是不是你要找的类型,该方法接收两个参数,一个是具体的错误,另一个是想要找的类型的一个指标,成功的话,说明在错误链上打到了对应类型错误并赋值给了这个指针。所以前面在讲自定义错误的时候,除了使用
err.(type)
来还原错误本身的类型,也可以用这里提到的As
。同理,也可以自己实现自定义错误的
As
方法来决定其中的细节,不过一般场景不涉及。总结:
[errors.Is](http://errors.Is)
来查找具体某个错误值[errors.As](http://errors.As)
来判断具体某个错误类型结合
defer
来包装错误函数中如果有多个地方都需要进行同样的错误包装,比如像下面这样:
联想到之前介绍过的
defer
可做一些收尾工作,可利用其在函数返回时来统一进行错误包装,优化掉这里的冗余代码:注意使用
defer
进行返回时,需要为返回变量声明名称,这样才能在defer
中使用。发生 panic 及恢复
Go 程序中发生异常时会生成 panic,比如试图访问超出 slice 边界的元素,内存溢出等。
除了 runtime 会发生 panic,程序中也可根据需要来生成:
运行结果:
可通过
recover
来处理 panic,结合defer
来用,让程序优雅退出而不是崩溃:运行结果:
与其他语言的 try catch 不同,发生 panic 后不建议通过 recover 让程序还继续运行,而是需要根据 panic 进行对应善后。比如,如果是内存溢出,在 recover 时及时落日志上报监控保存现场然后通过
os.Exit(1)
退出程序。不应该通过 panic recover 模式来获取出错的堆栈信息,recover 只是单纯提供一种在发生错误时,将信息打印出来,然后程序能够正常继续而不崩掉的机制。如果想打印出错误时的堆栈信息,一是可以通过包装错误自己实现,二是可用现成三方库。使用
fmt.Printf
时,也可通过%+v
打印 stack trace,但注意此时会打印程序文件的绝对路径,真实生产环境中一般想要隐藏掉这些信息,可在编译时加上-trimpath
参数。The text was updated successfully, but these errors were encountered: