字符串类型的错误
// simple string-based error
err1 := errors.New("math: square root of negative number")
// with formatting
err2 := fmt.Errorf("math: square root of negative number %g", x)
自定义错误
自定义错误可携带额外信息,比如错误码等。
先定义一个错误码枚举:
// 定义枚举前需要先定义类型
type Status int
// 定义枚举
const (
InvalidLogin = iota + 1
NotFound
)
然后使用上面的枚举来实现自定义错误:
type StatusErr struct {
Status Status
Message string
}
func (se StatusErr) Error() string {
return se.Message
}
测试:
func foo() error {
id := 1000
return StatusErr{
Status: InvalidLogin,
Message: fmt.Sprintf("status error with user id %d", id),
}
}
func main() {
err := foo()
switch e := err.(type) {
case StatusErr:
fmt.Println("error with status code:", e.Status)
default:
fmt.Println(e)
}
}
// error with status code: 1
上面示例代码中,注意两点:
error哨兵错误
ErrErrorFormatfunc main() {
data := []byte("blahblah...")
notAZipFile := bytes.NewReader(data)
_, err := zip.NewReader(notAZipFile, int64((len(data))))
if err == zip.ErrFormat {
fmt.Println("invalid file type!")
}
}
error// /xxx/go/1.16.5/libexec/src/archive/zip/reader.go
var (
ErrFormat = errors.New("zip: not a valid zip file")
ErrAlgorithm = errors.New("zip: unsupported compression algorithm")
ErrChecksum = errors.New("zip: checksum error")
)
Error 的包装
接收到 error 后,可添加点信息在上面,再将其返回出去。这样可形成一条错误链。
fmt.Errorf%werrorfunc bar() error {
err := foo()
return fmt.Errorf("some more info on err, the source err is :%w", err)
}
%verrors.Unwrapfunc main() {
err := bar()
if err != nil {
fmt.Println(err)
if sourceErr := 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
Unsraptype StatusError struct {
Status Status
Message string
+ err error
}
+ func (se StatusError) Unwrap()error{
+ return se.err
+ }
IsAs[errors.Is](http://errors.Is)[errors.As](http://errors.As)func fileChecker(name string) error {
f, err := os.Open(name)
if err != nil {
return fmt.Errorf("in fileChecker: %w", err)
}
defer f.Close()
return nil
}
func main() {
err := fileChecker("xxx.txt")
if err != nil {
if errors.Is(err, os.ErrNotExist) {
fmt.Println("file not exit")
}
}
}
errors.IsUnwrapIstype MyErr struct {
Status int
}
func (me MyErr)Error()string{
return fmt.Sprintf("error code: %d",me.Status)
}
func (me MyErr) Is(target error)bool{
if me2,ok:=target.(MyErr);ok{
return reflect.DeepEqual(me,me2)
}
return false
}
Asfunc main() {
err:=AFuncThatReturnsAnErr()
var myErr MyErr
if errors.As(err,&myErr){
fmt.Println(myErr.Code)
}
}
err.(type)AsAs总结:
[errors.Is](http://errors.Is)[errors.As](http://errors.As)defer函数中如果有多个地方都需要进行同样的错误包装,比如像下面这样:
func foo(i int)(string,error) {
_,err:=f1(i)
if err=nil{
return "",fmt.Errorf("in foo:%w",err)
}
_,err:=f2(i)
if err=nil{
return "",fmt.Errorf("in foo:%w",err)
}
_,err:=f3(i)
if err=nil{
return "",fmt.Errorf("in foo:%w",err)
}
return "",nil
}
deferfunc foo(i int)(_ string,err error) {
defer func() {
if err!=nil{
err=fmt.Errorf("in foo:%w",err)
}
}()
_,err:=f1(i)
if err=nil{
return "",err
}
_,err:=f2(i)
if err=nil{
return "",err
}
return f3(i)
}
deferdefer发生 panic 及恢复
Go 程序中发生异常时会生成 panic,比如试图访问超出 slice 边界的元素,内存溢出等。
除了 runtime 会发生 panic,程序中也可根据需要来生成:
func genPanic(msg string) {
panic(msg)
}
func main() {
genPanic("blah")
}
运行结果:
$ go run main.go
panic: blah
goroutine 1 [running]:
main.genPanic(...)
/xxx/main.go:4
main.main()
/xxx/main.go:8 +0x50
exit status 2
make: *** [run] Error 1recoverdeferfunc div60(i int) {
defer func() {
if v := recover(); v != nil {
fmt.Println(v)
}
}()
fmt.Println(60 / i)
}
func main() {
for _, val := range []int{1, 2, 0, 6} {
div60(val)
}
}
运行结果:
go run main.go
60
30
runtime error: integer divide by zero
10os.Exit(1)fmt.Printf%+v-trimpath