在Go中创建自定义错误
引言
Go提供了两种在标准库中创建错误的方法,[errors.New和fmt.Errorf],当与用户交流更复杂的错误信息时,或在调试时与未来的自己交流时,有时这两种机制不足以充分捕获和报告所发生的情况。为了传达更复杂的错误信息并实现更多的功能,我们可以实现标准库接口类型,error。
其语法如下:
type error interface {Error() string
}
builtin包将error定义为一个接口,它只有一个Error()方法,返回一个字符串形式的错误消息。通过实现这个方法,我们可以将定义的任何类型转换为我们自己的error。
让我们尝试运行以下示例来查看error接口的实现:
package mainimport ("fmt""os"
)type MyError struct{}func (m *MyError) Error() string {return "boom"
}func sayHello() (string, error) {return "", &MyError{}
}func main() {s, err := sayHello()if err != nil {fmt.Println("unexpected error: err:", err)os.Exit(1)}fmt.Println("The string:", s)
}
Outputunexpected error: err: boom
exit status 1
在这里,我们创建了一个新的空结构类型MyError,并在其上定义了Error()方法。Error()方法返回字符串"boom"。
在main()中,我们调用函数sayHello,该函数返回一个空字符串和一个新的MyError实例。由于sayHello总是会返回错误,所以main()中的if语句体中的fmt.Println调用总是会执行。然后,我们使用fmt.Println打印短前缀字符串"unexpected error:"以及err变量中保存的MyError实例。
请注意,我们不必直接调用Error(),因为fmt包能够自动检测这是Error 的实现。它透明地调用Error()来获取字符串"boom",并将其与前缀字符串"unexpected Error: err:"连接起来。
在自定义错误中收集详细信息
有时候,自定义错误是捕获详细错误信息的最简洁方式。例如,假设我们想要捕获HTTP请求产生的错误的状态码;运行以下程序来查看error的实现,它允许我们清晰地捕获信息:
package mainimport ("errors""fmt""os"
)type RequestError struct {StatusCode intErr error
}func (r *RequestError) Error() string {return fmt.Sprintf("status %d: err %v", r.StatusCode, r.Err)
}func doRequest() error {return &RequestError{StatusCode: 503,Err: errors.New("unavailable"),}
}func main() {err := doRequest()if err != nil {fmt.Println(err)os.Exit(1)}fmt.Println("success!")
}
Outputstatus 503: err unavailable
exit status 1
在这个例子中,我们创建了一个新的RequestError实例,并使用标准库中的errors.New函数提供状态码和一个错误。然后像前面的例子一样,我们使用fmt.Println打印它。
在RequestError的Error()方法中,我们使用fmt.Sprintf函数来使用创建错误时提供的信息来构造一个字符串。
类型断言和自定义错误
error接口只公开了一个方法,但我们可能需要访问error实现的其他方法来正确处理错误。例如,我们可能有几个临时的error自定义实现,可以通过存在的 temporary()方法进行检索。
接口为类型提供了更广泛的方法集合,因此我们必须使用类型断言来更改view正在显示的方法,或者完全删除它。
下面的例子在之前的RequestError的基础上增加了一个Temporary()方法,该方法将表明调用者是否应该重试请求:
package mainimport ("errors""fmt""net/http""os"
)type RequestError struct {StatusCode intErr error
}func (r *RequestError) Error() string {return r.Err.Error()
}func (r *RequestError) Temporary() bool {return r.StatusCode == http.StatusServiceUnavailable // 503
}func doRequest() error {return &RequestError{StatusCode: 503,Err: errors.New("unavailable"),}
}func main() {err := doRequest()if err != nil {fmt.Println(err)re, ok := err.(*RequestError)if ok {if re.Temporary() {fmt.Println("This request can be tried again")} else {fmt.Println("This request cannot be tried again")}}os.Exit(1)}fmt.Println("success!")
}
Outputunavailable
This request can be tried again
exit status 1
在main()中,我们调用doRequest(),它会返回一个error接口给我们。我们首先打印 error()方法返回的错误消息。接下来,我们尝试使用类型断言re, ok := err.(*RequestError)来暴露RequestError中的所有方法。如果类型断言成功,那么我们使用Temporary()方法来查看此错误是否为临时错误。由于doRequest()设置的StatusCode是503,这与http.StatusServiceUnavailable匹配,因此返回true并导致打印" this request can be try again"。在实践中,我们会发送另一个请求,而不是打印一条消息。
包装错误
通常,错误会在程序之外产生,例如:数据库、网络连接等。这些错误提供的错误消息不能帮助任何人找到错误的根源。在错误消息的开头使用额外的信息包装错误,可以为成功调试提供一些必要的上下文。
下面的例子演示了我们如何将一些上下文信息附加到从其他函数返回的晦涩的error上:
package mainimport ("errors""fmt"
)type WrappedError struct {Context stringErr error
}func (w *WrappedError) Error() string {return fmt.Sprintf("%s: %v", w.Context, w.Err)
}func Wrap(err error, info string) *WrappedError {return &WrappedError{Context: info,Err: err,}
}func main() {err := errors.New("boom!")err = Wrap(err, "main")fmt.Println(err)
}
Outputmain: boom!
WrappedError是一个有两个字段的结构体:一个是string类型的上下文消息,另一个是error类型,WrappedError提供了更多的信息。当Error()方法被调用时,我们再次使用fmt.Sprintf来打印上下文消息,然后Error(fmt.Sprintf也知道隐式调用Error()方法)。
在main()中,我们使用errors.New创建一个错误,然后使用我们定义的wrap函数包装这个错误。这允许我们指出这个error是在"main"中生成的。此外,由于我们的WrappedError也是一个error,我们可以包装其他的WrappedError,这将允许我们看到一个链来帮助我们追踪错误的来源。在标准库的帮助下,我们甚至可以在错误中嵌入完整的堆栈跟踪。
总结
由于error接口只有一个方法,我们已经看到我们可以为不同的情况提供不同类型的错误。这可以涵盖从将多条信息作为错误的一部分进行沟通到实现指数回退的所有事情。虽然Go中的错误处理机制表面上看起来很简单,但我们可以使用这些自定义错误来处理常见和不常见的情况,从而实现相当丰富的处理。
Go还有另一种沟通意外行为的机制panics。在错误处理系列的下一篇文章中,我们将研究恐慌——它们是什么以及如何处理它们。
相关文章:
在Go中创建自定义错误
引言 Go提供了两种在标准库中创建错误的方法,[errors.New和fmt.Errorf],当与用户交流更复杂的错误信息时,或在调试时与未来的自己交流时,有时这两种机制不足以充分捕获和报告所发生的情况。为了传达更复杂的错误信息并实现更多的…...
Vue.js2+Cesium1.103.0 十三、通过经纬度查询 GeoServer 发布的 wms 服务下的 feature 对象的相关信息
Vue.js2Cesium1.103.0 十三、通过经纬度查询 GeoServer 发布的 wms 服务下的 feature 对象的相关信息 Demo <template><divid"cesium-container"style"width: 100%; height: 100%;"><div style"position: absolute;z-index: 999;bott…...
使用STM32怎么喂狗 (IWDG)
STM32F1 的独立看门狗(以下简称 IWDG)。 STM32F1内部自带了两个看门狗,一个是独立看门狗 IWDG,另一个是窗口看门狗 WWDG, 本章只介绍独立看门狗 IWDG,窗口看门狗 WWDG 会在后面章节介绍。 本章要实现的功能…...
GEE:计算和打印GEE程序的执行时间
作者:CSDN @ _养乐多_ 本文记录了计算和打印程序的执行时间的Google Earth Engine (GEE)代码,并举例说明。 大家在执行GEE代码的时候,有时候为了对比两个不同的脚本,不知道代码执行花费了多少时间。本文记录了打印代码执行时间的函数,并举了一个应用案例说明。可以知道…...
GDPU 数据结构 天码行空5
一、实验目的 1.掌握队列的顺序存储结构 2.掌握队列先进先出运算原则在解决实际问题中的应用 二、实验内容 仿照教材顺序循环队列的例子,设计一个只使用队头指针和计数器的顺序循环队列抽象数据类型。其中操作包括:初始化、入队…...
SQLAlchemy学习-12.查询之 order_by 按desc 降序排序
前言 sqlalchemy的query默认是按id升序进行排序的,当我们需要按某个字段降序排序,就需要用到 order_by。 order_by 排序 默认情况下 sqlalchemy 的 query 默认是按 id 升序进行排序的 res session.query(Project).all() print(res) # [<Project…...
如何轻松打造数字人克隆系统+直播系统?OEM教你快速部署数字人SaaS系统源码
数字人做为国内目前最热门的人工智能创业赛道,连BAT都在跑步入局,中小企业更是渴望不渴及。但随着我国数字人头部品牌企业温州专帮信息科技有限公司旗下灰豚AI数字人平台的开源。使得中小企业零门槛可以轻松打造灰豚AI数字人一模一样的平台。灰豚数字人A…...
药物滥用第四篇介绍
OXY: 羟考酮(Oxycodone,OXY),分子式为C18H21NO4,是一种半合成的蒂巴因衍生物。羟考酮为半合成的纯阿片受体激动药,其作用机制与吗啡相似,主要通过激动中枢神经系统内的阿片受体而起镇…...
Apache Doris (四十三): Doris数据更新与删除 - Update数据更新
🏡 个人主页:IT贫道_大数据OLAP体系技术栈,Apache Doris,Clickhouse 技术-CSDN博客 🚩 私聊博主:加入大数据技术讨论群聊,获取更多大数据资料。 🔔 博主个人B栈地址:豹哥教你大数据的个人空间-豹哥教你大数据个人主页-哔哩哔哩视频 目录 1. Update数据更新原理...
面试算法29:排序的循环链表
问题 在一个循环链表中节点的值递增排序,请设计一个算法在该循环链表中插入节点,并保证插入节点之后的循环链表仍然是排序的。 分析 首先分析在排序的循环链表中插入节点的规律。当在图4.15(a)的链表中插入值为4的节点时&…...
python中不可变类型和可变类型
不可变类型:修改之后内存存储地址不会发生改变 可变类型:修改之后内存存储地址发生改变 set...
vue3封装Axios库的 API 请求并使用拦截器来处理请求和响应
目录 为什么添加封装该部分? 具体代码: 对代码的解释: 如何使用? 为什么添加封装该部分? 简化发送 HTTP 请求的流程提供统一的错误处理机制支持用户状态管理和鉴权具备良好的扩展性和灵活性提高开发效率并使得代码…...
RK3588开发笔记(二):基于方案商提供sdk搭建引入mpp和sdk的宿主机交叉编译Qt5.12.10环境
若该文为原创文章,转载请注明原文出处 本文章博客地址:https://hpzwl.blog.csdn.net/article/details/133915614 红胖子网络科技博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬…...
rust学习——函数返回值
概念 Rust 中的函数定义以 fn 开始,后跟着函数名和一对圆括号。大括号告诉编译器函数体在哪里开始和结束。 特殊的地方——函数返回值 错误的写法 正解1 去掉分号 fn main() {let x plus_one(5);println!("The value of x is: {}", x); }fn plus_…...
【Cadence】配置文件cdsinit和cdsenv的使用
文件功能 .cdsinit文件:主要负责一些加载项的设置,一些脚本工具及一些快捷键 .cdsenv文件:主要负责一些环境变量或者参数的设置 文件位置: (参照以下文件使用) Virtuoso配置文件“.cdsenv”文件介绍和使…...
软考 系统架构设计师系列知识点之基于架构的软件开发方法ABSD(6)
接前一篇文章:软考 系统架构设计师系列知识点之基于架构的软件开发方法ABSD(5) 所属章节: 第7章. 系统架构设计基础知识 第5节. 特定领域软件体系结构 相关试题 1. 基于架构的软件设计(ABSD)强调由商业、…...
MATLAB常用命令大全,非常详细(持续更新中)
** MATLAB命令大全 ** 管理命令和函数 help 在线帮助文件 doc 装入超文本说明 what M、MAT、MEX文件的目录列表 type 列出M文件 lookfor 通过help条目搜索关键字 which 定位函数和文件 Demo 运行演示程序 Path 控制MATLAB的搜索路径…...
js笔试面试题5道附答案
/*** 题目1: 解析Cookie字符串转化为对象* 输入:foobar; equationE%3Dmc%5E2* 输出:{ foo: bar, equation: Emc^2 }* 测试: parseCookie(foobar; equationE%3Dmc%5E2)*/ function parseCookie(str) {} /*** 题目2: 找出对象中符合…...
4-k8s-部署springboot项目简单实践
文章目录 一、部署原理图二、部署实践 一、部署原理图 部门一般都有一个属于自己的私服gitlab服务器,由开发者开发代码,然后上传到私服gitlab然后使用调度工具,如jenkins,去gitlab拉去代码,编译打包,最后得…...
Ai数字人直播系统SaaS源码大开源,源码独立部署助力中小企业发展!
源码独立部署ai数字人直播系统,如果放在上半年的话没有数百万投资几乎是天方夜谭,连想做个数字人代理商少则投资十万多则数十万才能进得了代理门槛。在此期间,数字人市场一度出现了大批不良企业利用网上下载的视频合成源码二次包装后打着数字…...
变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析
一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...
1.3 VSCode安装与环境配置
进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件,然后打开终端,进入下载文件夹,键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...
江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命
在华东塑料包装行业面临限塑令深度调整的背景下,江苏艾立泰以一场跨国资源接力的创新实践,重新定义了绿色供应链的边界。 跨国回收网络:废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点,将海外废弃包装箱通过标准…...
什么是EULA和DPA
文章目录 EULA(End User License Agreement)DPA(Data Protection Agreement)一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA(End User License Agreement) 定义: EULA即…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...
优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...
鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南
1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发,使用DevEco Studio作为开发工具,采用Java语言实现,包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...
React---day11
14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...
Linux部署私有文件管理系统MinIO
最近需要用到一个文件管理服务,但是又不想花钱,所以就想着自己搭建一个,刚好我们用的一个开源框架已经集成了MinIO,所以就选了这个 我这边对文件服务性能要求不是太高,单机版就可以 安装非常简单,几个命令就…...
上位机开发过程中的设计模式体会(1):工厂方法模式、单例模式和生成器模式
简介 在我的 QT/C 开发工作中,合理运用设计模式极大地提高了代码的可维护性和可扩展性。本文将分享我在实际项目中应用的三种创造型模式:工厂方法模式、单例模式和生成器模式。 1. 工厂模式 (Factory Pattern) 应用场景 在我的 QT 项目中曾经有一个需…...
