11

Go 错误处理:用 panic 取代 err != nil 的模式

 3 years ago
source link: https://studygolang.com/articles/32386
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Go 错误处理:用 panic 取代 err != nil 的模式

煎鱼 · 大约14小时之前 · 66 次点击 · 预计阅读时间 2 分钟 · 不到1分钟之前 开始浏览    

若有任何问题或建议,欢迎及时交流和碰撞。我的公众号是 【脑子进煎鱼了】,GitHub 地址:https://github.com/eddycjy

前段时间我分享了文章 《先睹为快,Go2 Error 的挣扎之路》后,和一位朋友进行了一次深度交流,他给我分享了他们项目组对于 Go 错误处理的方式调整。

简单来讲,就是在业务代码中使用 panic 的方式来替代 “永无止境” 的 if err != nil。这就是今天本文的重点内容,我们一起来看看是怎么做,又有什么优缺点。

为什么想替换

在 Go 语言中 if err != nil 写的太多,还要管方法声明各种,嫌麻烦又不方便:

err := foo()
if err != nil {
     //do something..
     return err
}

err := foo()
if err != nil {
     //do something..
     return err
}

err := foo()
if err != nil {
     //do something..
     return err
}

err := foo()
if err != nil {
     //do something..
     return err
}

上述还是示例代码,比较直面。若是在工程实践,还得各种 package 跳来跳去加 if err != nil,总的来讲比较繁琐,要去关心整体的上下游。更具体的就不赘述了,可以翻看我先前的文章。

怎么替换 err != nil

不想写 if err != nil 的代码,方式之一就是用 panic 来替代他。示例代码如下:

func GetFish(db *sql.DB, name string) []string {
    rows, err := db.Query("select name from users where `name` = ?", name)
    if err != nil {
        panic(err)
    }
    defer rows.Close()

    var names []string
    for rows.Next() {
        var name string
        err := rows.Scan(&name)
        if err != nil {
            panic(err)
        }

        names = append(names, name)
    }

    err = rows.Err()
    if err != nil {
        panic(err)
    }

    return names
}

在上述业务代码中,我们通过 panic 的方式取代了 return err 的函数返回,自然其所关联的下游业务代码也就不需要编写 if err != nil 的代码:

func main() {
    fish1 := GetFish(db, "煎鱼")
    fish2 := GetFish(db, "咸鱼")
    fish3 := GetFish(db, "摸鱼")
    ...
}

同时在转换为使用 panic 模式的错误机制后,我们必须要在外层增加 recover 方法:

func AppRecovery() gin.HandlerFunc {
    return func(c *gin.Context) {
        defer func() {
            if err := recover(); err != nil {
                if _, ok := err.(AppErr); ok {
                    // do something...
                } else {
                    panic(err)
                }
            }
        }()
    }
}

每次 panic 后根据其抛出的错误进行断言,识别是否定制的 AppErr 错误类型,若是则可以进行一系列的处理动作。否则可继续向上 panic 抛出给顶级的 Recovery 方法进行处理。

这就是一个相对完整的 panic 错误链路处理了。

  • 从优点上来讲:

    • 整体代码结构看起来更加的简洁,仅专注于实现逻辑即可。
    • 不需要关注和编写冗杂的 if err != nil 的错误处理代码。
  • 从缺点上来讲:

    • 认知负担的增加,需要参加项目的每一个新老同学都清楚该模式,要做一个基本规范或培训。
    • 存在一定的性能开销,每次 panic 都存在用户态的上下文切换。
    • 存在一定的风险性,一旦 panic 没有 recover 住,就会导致事故。
    • Go 官方并不推荐,与 panic 本身的定义相违背,也就是 panicerror 的概念混淆。

在今天这篇文章给大家分享了如何使用 panic 的方式来处理 Go 的错误,其必然有利必有有弊,需要做一个权衡了。

你们团队有没有为了 Go 错误处理做过什么新的调整呢?欢迎在留言区交流和分享。

我的公众号

分享 Go 语言、微服务架构和奇怪的系统设计,欢迎大家关注我的公众号和我进行交流和沟通。

1460000038666437

最好的关系是互相成就,各位的点赞就是煎鱼创作的最大动力,感谢支持。


有疑问加站长微信联系(非本文作者)

280

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:1006366459


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK