【Go错误处理】

错误

在Go语言中, 错误是可以预期的,并且不是非常严重,不会影响程序的运行。对于这类问题,返回错误给调用者的方法,让调用者自行处理。

**error接口**

在Go语言中,错误是**通过内置 error 接口实现的**。它很简单,只有一个Error方法来返回具体的错误信息。
type error interface {
    Error() string
}


package errors
// New returns an error that formats as the given text.
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
}
一般而言,error接口用于当方法或者函数执行遇到错误时进行返回,而且是第二个值。通过这种方式,可以让调用者自己根据错误信息决定如何处理。

==提示==:方法和函数基本上差不多,区别在于有无接收者,所以方法和函数表达的是一个意思。

**error 工厂函数**

我们可以自定义函数返回错误信息给调用者。通过 **errors.New()**这个工厂函数生成错误信息,它接收一个字符串参数,返回一个error接口  

自定义error

工厂函数返回错误信息的方式只能传递一个字符串,如果想携带更多信息需要自定义error。**自定义error其实就是先定义一个新类型,比如结构体,然后让这个类型实现error接口。**
type commonError struct {
    errorCode int // 错误码
    errorMsg string // 错误信息
}

func (c *commonError) Error() stirng {
    return c.errorMsg
}

return 0, &commonError{
    errorCode : 1,
    errorMSg : "不能为空",
}
**error断言**

有了自定义error,可以携带更多的错误信息,需要把返回的error接口转换为自定义的错误类型,需要用到类型断言。**类型断言在error接口上的应用,也称之为error断言。**
sum, err := add(-1, 2)
if cm, ok := sum.(*commonError); ok {
    fmt.Println("错误代码为:",cm.errorCode,",错误信息为:",cm.errorMsg)
} else {
    fmt.Println(sum)
}

错误嵌套

**Error Wrapping**

error接口虽然比较简洁,但是功能相对较弱。如果**基于一个存在的error再生成一个error,这就是错误嵌套。**
// 自定义结构体
type MyError struct {
    err error 
    msg string
}

// 实现error接口,并在初始化MyError的时候传递存在的error和新的错误信息
func (e *MyError)Error() string {
    return e.err.Error() + e.msg
}

func test() {
    // err 是一个存在的错误
    newErr := MyError(err, "错误信息")
}
上述方式可以满足需求,但是很繁琐,需要自定义新的类型还要实现error接口。所以从**Go语言1.13版本开始,Go标准库新增了Error Wrapping 功能** ,让我们可以基于一个存在的error生成新的error,并且保留原error信息。
// Go语言没有提供Wrap函数,而是扩展了fmt.Errorf函数,增加一个%w,通过这种方式,可以生成wrapping error

e := errors.New("错误信息")
w := fmt.Errorf("wrap一个错误:%w", e)
**errors.Unwrap函数**
// error可以包括嵌套生成新error,也可以被解开,通过errors.Unwrap函数得到被嵌套的error
// Go语言提供 errors.Unwrap 用于获取被嵌套的error,获取原始错误
fmt.Println(errors.Unwrap(w))

// 输出
错误信息
**errors.Is函数**

由于Go语言的Error Wrapping功能,让人不知道返回的err是否被嵌套,又嵌套了几层?于是**Go语言提供了error.Is 函数,用来判断两个error是否是同一个**。
func Is(err, target error) bool
  • 如果err和target是同一个,返回true

  • 如果err是一个wrapping error,target也包含在这个嵌套error链中的话,返回true。

    errors.As函数

    同理,error嵌套之后,error断言也不能用了,所以Go语言为解决这个问题,提供了errors.As函数。所以我们要尽可能使用Is、As这些函数做判断和转换。

var cm *commonError

if errors.As(err, &cm) {
     fmt.Println("错误代码为:",cm.errorCode,",错误信息为:",cm.errorMsg)
} else {
   fmt.Println(sum)
}

Deferred函数

Go语言提供了defer函数,保证不管自定义的函数出现异常还是错误,都会被执行。**defer 关键字用于修饰一个函或者方法,使得该函数或者方法在返回前才会执行,也就是被延迟,但又可以保证一定会执行。**

**defer语句通常被用于成对操作**,如文件的打开和关闭,加锁和释放锁,连接的建立和断开等。不管多复杂的操作,都可以保证资源被正确的释放。
  • 一个方法或者函数中,可以有多个defer语句

  • 多个deger语句的执行顺序依照后进先出的原则

    defer有一个调用栈,越早定义的越靠近栈的底部,在执行defer语句的时候,会从栈顶弹出一个defer然后执行。

Panic异常

**Go语言是一门静态的强类型语言**,很多问题都尽可能在编译时捕获,但是有一些只能在运行时检查,比如数组越界、不同类型强制转换,这类运行时问题会引起panic异常。除了运行时可以产生panic异常,我们也可以自己抛出panic异常。比如数据库连接。

panic是Go语言内置的函数,可以接受interface{}类型的参数,也就是任何类型的值都可以传给panic函数。
// interface{} 是空接口的意思,在Go语言中代表任意类型
func painc(v interface{})
panic异常是一种非常严重的情况,会让程序终端执行,使程序崩溃,所以**如果不是影响程序运行的错误,不要使用panic,使用普通error即可。**

Recover捕获Panic异常

通常情况下,我们不对panic异常做任何处理,已然已经影响到程序运行了,就直接崩溃即可。但也有些特殊情况,比如要在程序崩溃前释放资源,这时候需要从panic异常中恢复,才能完成处理。

在Go语言中,可以通过内置的Recover函数恢复panic异常。因为在程序panic异常崩溃的时候,之后被defer修饰的函数才能被执行,所以**recover函数要结合defer关键字使用才能生效**。 
// 通过defer关键字 + 匿名函数 + recover 函数从panic异常中恢复

defer func() {
    if p := recover(); p != nil {
        fmt.Println(p)
    }
}()

// recover函数成功捕获panic异常,返回值是通过panic函数传递的参数值

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,490评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,581评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,830评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,957评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,974评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,754评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,464评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,357评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,847评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,995评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,137评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,819评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,482评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,023评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,149评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,409评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,086评论 2 355

推荐阅读更多精彩内容

  • 最佳实践 panic 程序在启动时,如果有强依赖的服务出现故障时,需要panic退出 在程序启动时,如果发现有配置...
    匿名回复123阅读 1,240评论 0 1
  • 前言 最近在对极客时间毛剑老师的 Go 进阶训练营进行重温和学习汇总,这是一门比较偏向于工程化以及原理层面的的课程...
    pseudoyu阅读 577评论 0 0
  • 【译文】 摘要 对于来自其他面向对象语言比如C#(Go并不是你所认知典型面向对象语言)的读者来说,错误和异常几乎等...
    Go语言由浅入深阅读 440评论 0 1
  • 总结来源于《go in practice》1、自定义struct,并实现error接口 这种错误处理,可以丰富er...
    Go语言由浅入深阅读 1,544评论 0 2
  • 在实际工程项目中,总是通过程序的错误信息快速定位问题,但是又不希望错误处理代码写的冗余而又啰嗦。Go语言没有提供像...
    drunkery阅读 856评论 0 0