您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > Go语言

Golang学习之error浅析

时间:2020-12-02 11:08:52  来源:  作者:

由于Golang的语言设计的原因,不管是不是愿意,每个golang开发者的几乎每一段代码都需要与error做缠斗。下面我就简单分析一下golang中的error相关。

转自:https://www.jianshu.com/p/606d0e60c58d

参考:Go语言中文文档:www.topgoer.com

error是什么?

首先需要明确的一点是,golang中对于error类型的定义是什么?不同于很多语言的exception机制,golang在语言层面经常需要显示的做错误处理。其实从本质上来讲,golang中的error就是一个接口:

// The error built-in interface type is the conventional interface for
// representing an error condition, with the nil value representing no error.
type error interface {
    Error() string
}

和所有接口含义一样,nil表示零值。

before Go1.13

在golang的1.13版本之前,官方给到的错误处理方法寥寥无几,只有用来构造无额外参数的错误的errors.New和构造带额外参数的错误的fmt.Errorf。当时,经常需要使用标准库之外的扩展库来支持更丰富发错误构造和处理,比如由Dave Cheney主导的github.com/pkg/errors。
这些额外的error库主要的关注点在于提供方法用于描述错误的层级。回到上面的错误本身的定义,只是一个包含Error方法的接口,本身缺乏对于类似其他语言中类似traceback的描述能力,无法追踪错误的详细栈信息。
而以github.com/pkg/errors为代表的库,通过实现Wrap和Cause方法对来提供了包装/拆包错误的能力,提供了类似traceback(但需要开发者自己定义额外信息)和逐层解析并比较错误的能力。通过这个方法对,我们可以实现下面的用例:

// 为错误提供更丰富的上下文信息,方便定位错误
if _, err := ioutil.ReadAll(r);err != nil {
        return errors.Wrap(err, "read file failed")
}

// 判断错误的根错误是什么,根据最初的错误类型判断需要走什么错误处理逻辑
switch err := errors.Cause(err).(type) {
case *io.EOF:
        // handle specifically
default:
        // unknown error
}

After Go1.13

对于上面描述的错误处理,相比于较为成熟的exception处理模式,天生缺乏错误栈信息的缺点让很多开发者非常不满,虽然第三方库或多或少的弥补了这个缺点,但是作为开发中占比非常大的一部分代码,官方库的缺乏支持还是令人不满。所以Go team在1.13版本中进一步完善了错误相关的官方库支持。
首先,提供了%w构造方法和errors.Unwrap的方法对来支持类似Wrap和Cause相关的能力。

// 为错误提供更丰富的上下文信息,方便定位错误
if _, err := ioutil.ReadAll(r);err != nil {
        return fmt.Errorf("read file failed with err:%w", err)
}

// 判断错误的根错误是什么,根据最初的错误类型判断需要走什么错误处理逻辑
rawErr := errors.Unwrap(err)

不仅如此,官方库还带来了两个错误比较相关的API:

if errors.Is(err, io.EOF){
    ...
}

var eof io.EOF
if errors.As(err, &eof){
    ...
}

其中,errors.Is方法会逐层调用Unwrap方法,去和目标 err做比较,知道没有Unwrap方法或者err比较成功。errors.As方法的作用类似于之前的针对错误的类型断言。
至此,golang官方库提供了错误的构造方法,错误的比较方法,额外信息包装的能力,总体来说应该算是比较完善了。
关于Go1.13错误处理相关的实现,可以参考。

夭折的try

另外一个小小的番外插曲,曾经有一个呼声颇高的错误处理相关的提案:引入try关键字来增强错误处理的能力。主要使用方法如下:

// 包装调用方法
readFile := try(ioutil.ReadAll(r))
...
// 函数层级统一
defer func(){
    if err!=nil{
        switch err.(type){
            ...
        }  
    }
}()

带来的便利是减少了大量的if err!=nil语句,提供函数层级的统一错误处理处(一般在defer处)。然而最后由于可读性和显式处理错误的种种原因,这个提案被拒绝了。
更近一步的信息可以参考github上相关的讨论 和设计文档。

实践

基于go1.13提出的现有错误处理工具,我们大概能够采用下面的实践来进行错误处理:

  1. 针对基础错误类型,一般通过直接声明变量或者自定义结构:
// 常规的无额外参数的error
var BasicErr1 = errors.New("this is a basic error.")

func fn() error{
    ...
    if conditionA{
        return BasicErr
    }
}

// 调用处
if err!=nil{
    if errors.Is(err, BasicErr1){
        ...
    }
}

// 带参数信息的错误
type CustomErr struct {
    Code int64
    Msg string
}

func (e CustomErr)Error() string{
    return fmt.Sprintf("%d:%s", e.Code, e.Msg)
}

func fn() error{
    ...
    if conditionA{
        return CustomErr{Code: 123, Msg: "test"}
    }
}

// 调用处
if err!=nil{
    if e,ok:=err.(CustomErr);ok{
        ...
    }
}
  1. 对于调用三方库获取的报错,一般将额外信息(比如调用参数,上下文信息等方便定位问题的信息)包装之后向上层调用方直接抛出:
if _,err:=ioutil.ReadAll(r);err!=nil{
    return fmt.Errorf("read file failed:%w", err)
}

// 调用方
if err!=nil{
    if errors.Is(err, io.EOF){
        ...
    }
}

关于错误日志的处理部分,为了防止处处打日志造成的上下文信息分散和大量信息冗余,一般建议的处理方式是对于内部方法的调用,使用%w包装错误和必要的额外信息,直接返回到上层;对于最外层方法(一般是http handler或者rpc handler),将错误包装上下文,打印到错误日志中,再使用errors.Is或者errors.As方法,根据错误类型进行不同的错误处理逻辑。这样的好处是,对于全局而言,有且只有最外层一份错误日志,而这个错误信息时包装了层层调用信息的,内容最为齐全。



Tags:Golang   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
golang context 很好用,就使用php实现了github地址 : https://github.com/qq1060656096/php-go-context context使用闭坑指南1. 将一个Context参数作为第一个参数传递给传入和...【详细内容】
2021-11-05  Tags: Golang  点击:(41)  评论:(0)  加入收藏
简介工作中经常有定时执行某些代码块的需求,如果是PHP代码,一般写个脚本,然后用Cron实现。Go里提供了两种定时器:Timer(到达指定时间触发且只触发一次)和 Ticker(间隔特定时间触发)...【详细内容】
2021-05-10  Tags: Golang  点击:(310)  评论:(0)  加入收藏
不管你学没学过golang,都不妨碍这个21世纪的c语言正变得越来越流行,越来越多的平台服务使用golang来构建,我们熟知的docker就是采用golang语言进行开发设计的。谷歌作为golang...【详细内容】
2021-05-10  Tags: Golang  点击:(391)  评论:(0)  加入收藏
之前用 go 写一个小工具的时候, 用到了多个协程之间的通信, 当时随手查了查, 结果查出来一大坨, 简单记录一下. golang中多个协程之间是如何进行通信及数据同步的嘞.共享变...【详细内容】
2021-02-25  Tags: Golang  点击:(423)  评论:(0)  加入收藏
12月初,我们发现了一种新的用Golang编写的蠕虫。该蠕虫延续了 Golang在2020年流行的多平台恶意软件趋势。...【详细内容】
2021-01-05  Tags: Golang  点击:(174)  评论:(0)  加入收藏
本文主要研究一下golang的zap的ReflectType sweetenFieldszap@v1.16.0/sugar.gofunc (s *SugaredLogger) sweetenFields(args []interface{}) []Field { if len(args) ==...【详细内容】
2020-12-22  Tags: Golang  点击:(64)  评论:(0)  加入收藏
Golang的匿名结构是什么?匿名结构就像普通结构一样,但是它没有名称定义,因此不能在代码的其他地方引用。Go中的结构类似于C等其他语言中的结构。它们是字段的类型化集合,用于将...【详细内容】
2020-12-17  Tags: Golang  点击:(139)  评论:(0)  加入收藏
前文《理解 Paxos》只包含伪代码,帮助了理解但又不够爽,既然现在都讲究 Talk is cheap. Show me the code.这次就把文章中的伪代码用 Go 语言实现出来,希望能帮助各位朋友更直...【详细内容】
2020-12-15  Tags: Golang  点击:(116)  评论:(0)  加入收藏
今天逛github超市时,发现一个非常不错的数据可视化库go-echarts,特分享给大家。介绍在 Golang 这门语言中,目前数据可视化的第三方库还是特别少,go-echarts的开发就是为了填补这...【详细内容】
2020-12-15  Tags: Golang  点击:(88)  评论:(0)  加入收藏
由于Golang的语言设计的原因,不管是不是愿意,每个golang开发者的几乎每一段代码都需要与error做缠斗。下面我就简单分析一下golang中的error相关。...【详细内容】
2020-12-02  Tags: Golang  点击:(85)  评论:(0)  加入收藏
▌简易百科推荐
zip 是一种常见的归档格式,本文讲解 Go 如何操作 zip。首先看看 zip 文件是如何工作的。以一个小文件为例:(类 Unix 系统下)$ cat hello.textHello!执行 zip 命令进行归档:$ zip...【详细内容】
2021-12-17  Go语言中文网    Tags:Go语言   点击:(12)  评论:(0)  加入收藏
大家好,我是 polarisxu。前段时间,Russ Cox 明确了泛型相关的事情,原计划在标准库中加入泛型相关的包,改放到 golang.org/x/exp 下。目前,Go 泛型的主要设计者 ianlancetaylor 完...【详细内容】
2021-11-30  Go语言中文网    Tags:slices 包   点击:(24)  评论:(0)  加入收藏
前言最近因为项目需要写了一段时间的 Go ,相对于 Java 来说语法简单同时又有着一些 Python 之类的语法糖,让人大呼”真香“。 但现阶段相对来说还是 Python 写的多一些,偶尔还...【详细内容】
2021-11-25  crossoverJie    Tags:Go   点击:(29)  评论:(0)  加入收藏
go-micro是基于 Go 语言用于开发的微服务的 RPC 框架,主要功能如下:服务发现,负载均衡 ,消息编码,请求/响应,Async Messaging,可插拔接口,最后这个功能牛p安装步骤安装proto...【详细内容】
2021-09-06    石老师小跟班  Tags:go-micro   点击:(196)  评论:(0)  加入收藏
GoLand 2021.2 EAP 5 现已发布。用户可以从工具箱应用程序中获得 EAP 构建,也可以从官方网站手动下载。并且从此 EAP 开始,只有拥有有效的 JetBrains 帐户才能加入该计划。手...【详细内容】
2021-06-29  IT实战联盟  今日头条  Tags:GoLand   点击:(185)  评论:(0)  加入收藏
作者:HDT3213今天给大家带来的开源项目是 Godis:一个用 Go 语言实现的 Redis 服务器。支持: 5 种数据结构(string、list、hash、set、sortedset) 自动过期(TTL) 发布订阅、地理位...【详细内容】
2021-06-18  HelloGitHub  今日头条  Tags:Go   点击:(125)  评论:(0)  加入收藏
统一规范篇合理规划目录本篇主要描述了公司内部同事都必须遵守的一些开发规矩,如统一开发空间,既使用统一的开发工具来保证代码最后的格式的统一,开发中对文件和代码长度的控制...【详细内容】
2021-05-18  1024课堂    Tags:Go语言   点击:(232)  评论:(0)  加入收藏
闭包概述 闭包不是Go语言独有的概念,在很多编程语言中都有闭包 闭包就是解决局部变量不能被外部访问的一种解决方案 是把函数当作返回值的一种应用 代码演示总体思想:在函数...【详细内容】
2021-05-14  HelloGo  今日头条  Tags:Go语言   点击:(223)  评论:(0)  加入收藏
一时想不开,想了解一下Go语言,于是安装了并体验了一下。下载1. 进入golang.google.cn 点击Download Go 2.选择对应的操作系统,点击后开始下载。 安装1. windows下执行傻瓜式安...【详细内容】
2021-05-12  程序员fearlazy  fearlazy  Tags:Go语言   点击:(236)  评论:(0)  加入收藏
1.简介channel是Go语言的一大特性,基于channel有很多值得探讨的问题,如 channel为什么是并发安全的? 同步通道和异步通道有啥区别? 通道为何会阻塞协程? 使用通道导致阻塞的协程...【详细内容】
2021-05-10  程序员麻辣烫  今日头条  Tags:Go通道   点击:(272)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条