[tldr] GO语言异常处理
在开发中, 处理异常是很重要的, 考虑各种错误情况并且提出对应的解决办法是保证不出大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语言异常处理的更多相关文章
- 第63课 C语言异常处理
1. 异常的概念 (1)程序在运行过程中可能产生异常 (2)异常(Exception)与Bug的区别 ①异常是程序运行时可预料的执行分支 ②Bug是程序是的错误,是不被预期的运行方式 2. 异常和Bu ...
- go语言异常处理
go语言异常处理 error接口 go语言引入了一个关于错误错里的标准模式,即error接口,该接口的定义如下: type error interface{ Error() string } 对于要返 ...
- Go语言异常处理defer\panic\recover
Go语言追求简洁优雅,所以,Go语言不支持传统的 try…catch…finally 这种异常,因为Go语言的设计者们认为,将异常与控制结构混在一起会很容易使得代码变得混乱.因为开发者很容易滥用异常, ...
- c语言异常处理机制
异常处理机制:setjmp()函数与longjmp()函数 C标准库提供两个特殊的函数:setjmp() 及 longjmp(),这两个函数是结构化异常的基础,正是利用这两个函数的特性来实现异常. 所 ...
- C语言异常处理和连接数据库
#include <stdio.h> #include <setjmp.h> jmp_buf j; void Exception(void); double diva(doub ...
- C语言异常处理之 setjmp()和longjmp()
异常处理之除0情况 相信大家处理除0时,都会通过函数,然后判断除数是否为0,代码如下所示: double divide(doublea,double b) { const double delta = ...
- C语言异常处理编程的三个境界
http://blog.csdn.net/treefish2012/article/details/17466487 这是上一次看完Herb Sutter的<Exceptional C++> ...
- C语言异常处理
异常的概念-程序在运行过程中可能产生异常-异常(Exception)与Bug的区别 异常是程序运行时可预料的执行分支 Bug是程序中的错误,是不被预期的运行方式 异常(Exception)和Bug的对 ...
- GO语言异常处理03---自定义异常
package main import ( "fmt" "time" ) /* type error interface { Error() string } ...
- GO语言异常处理02---返回错误
package main import ( "errors" "fmt" "math" ) /*计算圆面积,同时返回[结果-错误对]*/ f ...
随机推荐
- vue-element-template实现顶部菜单栏
一.框架侧边栏改为顶部导航栏 1.复制src/layout/componets/Sidebar所有文件至同级目录,改名为Headbar 2.src/layout/components/index.js ...
- 深入掌握 SQL 深度应用:复杂查询的艺术与技巧
title: 深入掌握 SQL 深度应用:复杂查询的艺术与技巧 date: 2025/2/10 updated: 2025/2/10 author: cmdragon excerpt: SQL(结构化 ...
- 攻城攻心的混淆大师——深入解析第十八届CISCN x 第二届长城杯初赛Reverse赛题vt
前言 在初赛结束近两月之际,笔者在复盘过程中意外发现了这道当时无人能解的难题.经过两日深入的探索与钻研,笔者终于成功地对这道赛题进行了全面的解构.在品味破译flag所带来的喜悦之余,笔者亦深感此题蕴含 ...
- CF2043C Sums on Segments
题意概要 一个数组,最多有一个数的绝对值不是 \(1\),求出所有可以得到的区间和. 思路 这里提供一个 数据结构优化查询前缀和最值 的做法. 最多有一个数的绝对值不是 \(1\),那我们可以先忽略掉 ...
- 算子var_threshold
算子var_threshold 名称 var_threshold - 通过局部均值和标准差分析对图像进行阈值处理. 签名 var_threshold(Image : Region : MaskWidt ...
- 【问题】抓取微信公众号文章 特殊字符()问题
最初对抓取的文章处理使用正则表达式统一处理,代码如下: Regex regex_p = new Regex(@"<p>\s*</p>", RegexOpti ...
- Anaconda使用记录
1 安装 windows下,安装完添加环境变量(哦安装时勾选添加环境变量选项就是加这些变量的) ## (记anaconda软件目录为%ANACONDA3%) %ANACONDA3%\ %ANACOND ...
- Vulnhub-venom
对于该靶机,注意利用了信息收集来的21端口和80端口,网站源码发现账户,ftp匿名登录密码猜测,维吉尼亚解密,后台管理员登录,CVE文件上传RCE漏洞利用反弹shell,提权有两中,利用版本内核提权和 ...
- vue学习一(指令2.v-bind,v-model)
2.1.v-bind: 单向绑定(修改数据项,标签内容也改变:修改标签内容,数据项不会改变) 给html标签的属性绑定,可以用来动态修改class,简写 v-bind:style = ...
- PVE虚拟机安装详解
前言 PVE,全称Proxmox Virtual Environment,是基于Debian的Linux系统,虚拟机内核为KVM.硬件兼容性优秀.界面功能不强,很多操作要靠命令行,但扩展能力几乎是无限 ...