在开发中, 处理异常是很重要的, 考虑各种错误情况并且提出对应的解决办法是保证不出大BUG的重要之处.

error in go

GO语言的异常是很简单的, 只需要实现Error函数接口即可

func (e ErrA) Error() string {
return "ErrA"
}

过于简单的实现, 和C一样

errors

这是GO的一个库, 专门用来处理异常

Go语言的标准库一般会在后面加一个s代表是处理这种类型的标准库, 例如strings

异常处理

error类别判断

类型断言实现类别判断

Go语言提供了类型断言的方式, 针对接口, 可以实现不同类型之间的相互转换

这就是为什么errA需要是error类型, 因为是接口类型才能转换

	var errA error = ErrA{}
var errB error = ErrB{} fmt.Println(errA.Error())
fmt.Println(errB.Error()) if e, ok := errA.(ErrB); ok {
fmt.Println(e)
fmt.Println("found ErrB")
} else {
fmt.Println("not found ErrB")
}
  • 这个简单好用, 如果需要嵌套的异常处理, 那么应该选择使用这个.
  • 当然可以使用switch语句针对多种错误类型进行处理, 针对多个类型的时候使用这个方式可以更好的处理
func main() {
var errA error = ErrA{}
var errB error = ErrB{wrappedErr: errA} for err := errB; err != nil; err = errors.Unwrap(err) {
fmt.Println(err)
switch err.(type) {
case ErrA:
fmt.Println("ErrA")
case ErrB:
fmt.Println("ErrB")
default:
fmt.Println("default")
}
fmt.Println()
}
}

上述代码是使用switch配合类型断言来处理异常

errors函数类别判断

通过errors.Is或者errors.As函数实现类别的判断

Go语言官方提供的异常处理, 也只是简单的封装.

  • 这个函数的本质是检查这个error是否包含某个error(一个error可以包含多个error, 嵌套error)
	var errA error = ErrA{}
var errB error = ErrB{} fmt.Println(errA.Error())
fmt.Println(errB.Error()) if errors.Is(errA, errB) {
fmt.Println("errA is errB")
} else {
fmt.Println("errA is not errB")
}

使用这个函数让代码看起来十分的简洁

  • 使用errors提供的函数在处理嵌套的error的时候可能存在问题, 我们在下面讨论

嵌套error

有些时候我们需要把error一层一层wrap起来然后向上抛出异常进行处理. 像是多层的Throw错误.

errors库提供了Unwrap函数, 如果error实现了这个接口, 那么就可以实现error嵌套

package main

import (
"errors"
"fmt"
) type ErrA struct {
wrappedErr error
} func (e ErrA) Error() string {
return fmt.Sprintf("ErrA -> %v", e.wrappedErr)
} func (e ErrA) Unwrap() error {
return e.wrappedErr
} type ErrB struct {
wrappedErr error
} func (e ErrB) Error() string {
return fmt.Sprintf("ErrB -> %v", e.wrappedErr)
} func (e ErrB) Unwrap() error {
return e.wrappedErr
} func main() {
errA := ErrA{}
errB := ErrB{} fmt.Println(errA.Error())
fmt.Println(errB.Error()) if errors.Is(errA, ErrB{}) {
fmt.Println("errA is ErrB")
} else {
fmt.Println("errA is not ErrB")
} fmt.Println() var wrapErr error = ErrA{wrappedErr: ErrB{}}
for err := wrapErr; err != nil; err = errors.Unwrap(err) {
fmt.Println(err)
}
}

可以使用for循环语句来解开error

但是这个for循环拿出来的error使用errors.Is函数是无法准确识别当前的error属于哪个类别的, 因为所有被wrap的error类别都可以被识别到.

当然也可以通过errors库提供的Join函数实现合并多个error的功能, var wrappedErr error = errors.Join(errA, errB)语句提供了合并多个error的方式.

合并之后的error实现的Unwrap接口是返回[]error的, 但是在errors库使用的时候, 会被视为同一个error

可以通过errors.As或者errors.Is判断当前的错误是否包含某一个特定的错误

errors处理嵌套error

当然可以使用errors库来处理嵌套的error, 因为只要符合error接口规范的都可以使用errors进行处理

但是存在一些不方便的地方, 比如, 一个嵌套ErrA的ErrB错误对象, 同时可以被认为是ErrA和ErrB类型. 下面是一个例子

package main

import (
"errors"
"fmt"
) type ErrA struct {
wrappedErr error
} func (e ErrA) Error() string {
return fmt.Sprintf("ErrA -> %v", e.wrappedErr)
} func (e ErrA) Unwrap() error {
return e.wrappedErr
} type ErrB struct {
wrappedErr error
} func (e ErrB) Error() string {
return fmt.Sprintf("ErrB -> %v", e.wrappedErr)
} func (e ErrB) Unwrap() error {
return e.wrappedErr
} func main() {
var errA error = ErrA{}
var errB error = ErrB{wrappedErr: errA} for err := errB; err != nil; err = errors.Unwrap(err) {
fmt.Println(err)
if errors.Is(err, errA) {
fmt.Println("errA is the cause of the error")
}
fmt.Println()
}
}

结果是

无法识别当前这一层的error是ErrB

造成这个问题的原因是, errors.Is的源码实现如下:

func is(err, target error, targetComparable bool) bool {
for {
if targetComparable && err == target {
return true
}
if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) {
return true
}
switch x := err.(type) {
case interface{ Unwrap() error }:
err = x.Unwrap()
if err == nil {
return false
}
case interface{ Unwrap() []error }:
for _, err := range x.Unwrap() {
if is(err, target, targetComparable) {
return true
}
}
return false
default:
return false
}
}
}

如果遇上可以Unwrap的接口类型直接再次Unwrap去找是否存在一个类型是符合条件的

对此, 需要使用类型断言的方式来判断错误类型

func main() {
var errA error = ErrA{}
var errB error = ErrB{wrappedErr: errA} for err := errB; err != nil; err = errors.Unwrap(err) {
fmt.Println(err)
switch err.(type) {
case ErrA:
fmt.Println("ErrA")
case ErrB:
fmt.Println("ErrB")
default:
fmt.Println("default")
}
fmt.Println()
}
}

效果如下:

成功识别出了错误的类型

  • 如果需要准确识别错误的具体类型, 而不是检查这个报错包含哪些错误的话, 应该使用这个

参考

GO语言的errors库

Go语言(golang)新发布的1.13中的Error Wrapping深度分析

[tldr] GO语言异常处理的更多相关文章

  1. 第63课 C语言异常处理

    1. 异常的概念 (1)程序在运行过程中可能产生异常 (2)异常(Exception)与Bug的区别 ①异常是程序运行时可预料的执行分支 ②Bug是程序是的错误,是不被预期的运行方式 2. 异常和Bu ...

  2. go语言异常处理

    go语言异常处理 error接口 go语言引入了一个关于错误错里的标准模式,即error接口,该接口的定义如下: type error interface{ Error() string } 对于要返 ...

  3. Go语言异常处理defer\panic\recover

    Go语言追求简洁优雅,所以,Go语言不支持传统的 try…catch…finally 这种异常,因为Go语言的设计者们认为,将异常与控制结构混在一起会很容易使得代码变得混乱.因为开发者很容易滥用异常, ...

  4. c语言异常处理机制

    异常处理机制:setjmp()函数与longjmp()函数 C标准库提供两个特殊的函数:setjmp() 及 longjmp(),这两个函数是结构化异常的基础,正是利用这两个函数的特性来实现异常. 所 ...

  5. C语言异常处理和连接数据库

    #include <stdio.h> #include <setjmp.h> jmp_buf j; void Exception(void); double diva(doub ...

  6. C语言异常处理之 setjmp()和longjmp()

    异常处理之除0情况 相信大家处理除0时,都会通过函数,然后判断除数是否为0,代码如下所示: double divide(doublea,double b) { const double delta = ...

  7. C语言异常处理编程的三个境界

    http://blog.csdn.net/treefish2012/article/details/17466487 这是上一次看完Herb Sutter的<Exceptional C++> ...

  8. C语言异常处理

    异常的概念-程序在运行过程中可能产生异常-异常(Exception)与Bug的区别 异常是程序运行时可预料的执行分支 Bug是程序中的错误,是不被预期的运行方式 异常(Exception)和Bug的对 ...

  9. GO语言异常处理03---自定义异常

    package main import ( "fmt" "time" ) /* type error interface { Error() string } ...

  10. GO语言异常处理02---返回错误

    package main import ( "errors" "fmt" "math" ) /*计算圆面积,同时返回[结果-错误对]*/ f ...

随机推荐

  1. Hive explain执行计划详解

    简介:HIVE提供了EXPLAIN命令来展示一个查询的执行计划,这个执行计划对于我们了解底层原理,hive 调优,排查数据倾斜等很有帮助 一.EXPLAIN 参数介绍 语法 : EXPLAIN [EX ...

  2. Luogu P1930 亚瑟王的宫殿 题解 [ 蓝 ] [ 分层图最短路 ] [ 枚举 ]

    亚瑟王的宫殿:比较 tricky 的图论. 图论做法 思路 因为是无向图,所以考虑一个经典 trick,把所有点到集合点的距离之和化为集合点到其他所有点的位置之和,就可以从集合点做单源最短路了. 于是 ...

  3. P3306 [SDOI2013] 随机数生成器 题解

    传送门 题解 思路 由题目中可知: \[\large x_i \equiv ax_{i-1}+b\pmod{p} \] 可以得出: \[\large t=x_{n+1} \equiv a^nx_1+b ...

  4. 实战AI大模型辅助编程:新安江水文模型和SCE-UA优化算法的移植与实现

    新安江水文模型与 SCE-UA 优化算法是水文学和水资源管理领域的重要工具,二者结合使用可以有效模拟流域的水文过程并优化模型参数. 新安江水文模型是一种概念性水文模型,主要用于模拟流域的降雨-径流关系 ...

  5. 水利地理信息系统 - www.WaterGIS.cn

    用C#语言开发了一个免费GIS软件,可以用来快速浏览和编辑SHAPE文件,支持天地图等. 下载可以去QQ群:722489551

  6. 类的public, private, protected有什么区别?

    如何区别C# private和protected? 一.通过使用来区分 1.private:编程语句在模块级别中使用,用于声明私有变量及分配存储空间. 2.protected:编程语句在模块级别中使用 ...

  7. JavaScript 之 高级程序设计 基础篇 (一)

    导读 此篇文章为作者拜读JavaScrpit 第四版(红宝石)的笔记内容.适用于有经验的程序员阅读:作者 java开发出身.在之前前后端不分离的时代 使用esayUI JQuery的时代 经常写 js ...

  8. Processing多窗口程序范例(三)

    再来一例~另一种叠加方式呈现...看图: 程序 主程序: package syf.demo.multiwindow3; import processing.core.PApplet; import p ...

  9. day4-进制与位运算

    进制 对于整数有4种表现方式 二进制,满二进一 十进制,满10进1 八进制,满8进1,数字0开头 十六进制,满16进1,以0x或0X开头 进制转换 二进制转十进制 规则:从最低位开始(右边),将每位数 ...

  10. VUE-CLI 创建VUE3项目

    前言 第一篇当然是如何安装vue3 安装步骤 第一步安装vue-cli npm install -g @vue/cli // vue --version 第二步创建项目 vue create hell ...