Golang中的错误处理的示例详解
怎么入门Golang编程?需要学习哪些知识点?这是新手们刚接触编程时常见的问题;下面golang学习网就来给大家整理分享一些知识点,希望能够给初学者一些帮助。本篇文章就来介绍《Golang中的错误处理的示例详解》,涉及到错误、处理,有需要的可以收藏一下
1、panic
当我们执行panic的时候会结束下面的流程:
package main
import "fmt"
func main() {
fmt.Println("hello")
panic("stop")
fmt.Println("world")
}
输出:
go run 9.go
hello
panic: stop
但是panic也是可以捕获的,我们可以使用defer和recover实现:
package main
import "fmt"
func main() {
defer func() {
if r := recover(); r != nil {
fmt.Println("recover: ", r)
}
}()
fmt.Println("hello")
panic("stop")
fmt.Println("world")
}
输出:
go run 9.go
hello
recover: stop
那什么时候适合panic呢?在 Go 中,panic 用于表示真正的异常,例如程序错误。我们经常会在一些内置包里面看到panic的身影。
比如strings.Repeat重复返回一个由字符串 s 的计数副本组成的新字符串:
func Repeat(s string, count int) string {
if count == 0 {
return ""
}
//
if count
我们可以看到当重复的次数小于0或者重复count次之后s的长度溢出,程序会直接panic,而不是返回错误。这时因为strings包限制了error的使用,所以在程序错误时会直接panic。
还有一个例子是关于正则表达式的例子:
package main
import (
"fmt"
"regexp"
)
func main() {
pattern := "a[a-z]b*" // 1
compile, err := regexp.Compile(pattern) // 2
if err != nil { // 2
fmt.Println("compile err: ", err)
return
}
// 3
allString := compile.FindAllString("acbcdadb", 3)
fmt.Println(allString)
}
- 编写一个正则表达式
- 调用Compile,解析正则表达式,如果成功,返回用于匹配文本的 Regexp 对象。否则返回错误
- 利用正则,在输入的字符串中,获取所有的匹配字符
可以看到如果上面正则解析失败是可以继续往下执行的,但是regexp包中还有另外一个方法MustCompile:
func MustCompile(str string) *Regexp {
regexp, err := Compile(str)
if err != nil {
panic(`regexp: Compile(` + quote(str) + `): ` + err.Error())
}
return regexp
}
这个方法说明正则的解析是强依赖的,如果解析错误,直接panic结束程序。用户可以根据实际情况选择。
但是实际开发中我们还是要谨慎使用panic,因为它会使程序结束运行(除非我们调用defer recover)
2、包装错误
错误包装是将错误包装或者打包在一个包装容器中,这样的话我们就可以追溯到源错误。错误包装的主要作用就是:
- 为错误添加上下文
- 将错误标记为特定类型的错误
我们可以看一个访问数据库的例子:
package main
import (
"fmt"
"github.com/pkg/errors"
)
type Courseware struct {
Id int64
Code string
Name string
}
func getCourseware(id int64) (*Courseware, error) {
courseware, err := getFromDB(id)
if err != nil {
return nil, errors.Wrap(err, "六月的想访问这个课件") // 2
}
return courseware, nil
}
func getFromDB(id int64) (*Courseware, error) {
return nil, errors.New("permission denied") // 1
}
func main() {
_, err := getCourseware(11)
if err != nil {
fmt.Println(err)
}
}
- 访问数据库时我们返回了原始的错误信息
- 到上层我们添加了一些自定义的上下文信息
输出:
go run 9.go
六月的想访问这个课件: permission denied
当然我们也可以将错误包装成我们自定义类型的错误,我们稍微修改下上面的例子:
package main
import (
"fmt"
"github.com/pkg/errors"
)
type Courseware struct {
Id int64
Code string
Name string
}
// 1
type ForbiddenError struct {
Err error
}
// 2
func (e *ForbiddenError) Error() string {
return "Forbidden: " + e.Err.Error()
}
func getCourseware(id int64) (*Courseware, error) {
courseware, err := getFromDB(id)
if err != nil {
return nil, &ForbiddenError{err} // 4
}
return courseware, nil
}
func getFromDB(id int64) (*Courseware, error) {
return nil, errors.New("permission denied") // 3
}
func main() {
_, err := getCourseware(11)
if err != nil {
fmt.Println(err)
}
}
- 首先我们自定义了ForbiddenError的错误类型
- 我们实现了error接口
- 访问数据库抛出原始错误
- 上层返回ForbiddenError类型的错误
输出:
go run 9.go
Forbidden: permission denied
当然我们也可以不用创建自定义错误的类型,去包装错误添加上下文:
package main
import (
"fmt"
"github.com/pkg/errors"
)
type Courseware struct {
Id int64
Code string
Name string
}
func getCourseware(id int64) (*Courseware, error) {
courseware, err := getFromDB(id)
if err != nil {
return nil, fmt.Errorf("another wrap err: %w", err) // 1
}
return courseware, nil
}
func getFromDB(id int64) (*Courseware, error) {
return nil, errors.New("permission denied")
}
func main() {
_, err := getCourseware(11)
if err != nil {
fmt.Println(err)
}
}
使用%w包装错误
使用这的好处是我们可以追溯到源错误,从而方便我们做一些特殊的处理。
还有一种方式是使用:
return nil, fmt.Errorf("another wrap err: %v", err)
%v的方式不会包装错误,所以无法追溯到源错误,但往往有时候我们会选择这种方式,而不用%w的方式。%w的方式虽然能包装源错误,但往往我们会通过源错误去做一些处理,假如源错误被修改,那包装这个源错误的相关错误都需要做响应变化。
3、错误类型判断
我们扩展一下上面查询课件的例子。现在我们有这样的判断,如果传进来的id不合法我们返回400错误,如果查询数据库报错我们返回500错误,我们可以像下面这样写:
package main
import (
"fmt"
"github.com/pkg/errors"
)
type Courseware struct {
Id int64
Code string
Name string
}
type ForbiddenError struct {
Err error
}
func (e *ForbiddenError) Error() string {
return "Forbidden: " + e.Err.Error()
}
func getCourseware(id int64) (*Courseware, error) {
if id
输出:
go run 9.go
500 err: Forbidden: permission denied
这样看起来好像也没什么问题,现在我们稍微修改下代码,把上面ForbiddenError包装一下:
package main
import (
"fmt"
"github.com/pkg/errors"
)
type Courseware struct {
Id int64
Code string
Name string
}
type ForbiddenError struct {
Err error
}
func (e *ForbiddenError) Error() string {
return "Forbidden: " + e.Err.Error()
}
func getCourseware(id int64) (*Courseware, error) {
if id
输出:
go run 9.go
400 err: wrap err: Forbidden: permission denied
可以看到我们的Forbidden错误进到了400里面,这并不是我们想要的结果。之所以会这样,是因为在ForbiddenError的外面又包装了一层Error错误,使用类型断言的时候判断出来的是Error错误,所以进到了400分支。
这里我们可以使用errors.As方法,它会递归调用Unwrap方法,找到错误链中第一个与target匹配的方法:
package main
import (
"fmt"
"github.com/pkg/errors"
)
type Courseware struct {
Id int64
Code string
Name string
}
type ForbiddenError struct {
Err error
}
func (e *ForbiddenError) Error() string {
return "Forbidden: " + e.Err.Error()
}
func getCourseware(id int64) (*Courseware, error) {
if id
输出:
go run 9.go
500 err: wrap err: Forbidden: permission denied
4、错误值判断
在代码中或者mysql库或者io库中我们经常会看到这样的全局错误:
var ErrCourseware = errors.New("courseware")
这种错误我们称之为哨兵错误。一般数据库没查到ErrNoRows或者io读到了EOF错误,这些特定的错误可以帮助我们做一些特殊的处理。
一般我们会直接用==号判断错误值,但是就像上面的如果错误被包装哪我们就不好去判断了。好在errors包中提供了errors.Is方法,通过递归调用Unwrap判断错误链中是否与目标错误相匹配的错误值:
if err != nil {
if errors.Is(err, ErrCourseware) {
// ...
} else {
// ...
}
}
理论要掌握,实操不能落!以上关于《Golang中的错误处理的示例详解》的详细介绍,大家都掌握了吧!如果想要继续提升自己的能力,那么就来关注golang学习网公众号吧!
gocodereview代码调试
- 上一篇
- gocodereview代码调试
- 下一篇
- 详解Go语言中切片的长度与容量的区别
-
- 爱笑的大树
- 这篇技术文章真及时,细节满满,赞 ??,mark,关注up主了!希望up主能多写Golang相关的文章。
- 2023-02-05 08:04:28
-
- 害羞的网络
- 这篇文章出现的刚刚好,太细致了,很棒,已收藏,关注博主了!希望博主能多写Golang相关的文章。
- 2023-01-30 13:51:45
-
- 玩命的砖头
- 真优秀,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,帮助很大,总算是懂了,感谢博主分享文章内容!
- 2023-01-30 11:21:20
-
- 灵巧的短靴
- 这篇技术文章太及时了,太详细了,赞 ??,已加入收藏夹了,关注师傅了!希望师傅能多写Golang相关的文章。
- 2023-01-15 20:37:01
-
- 苗条的金针菇
- 太全面了,码起来,感谢作者的这篇技术文章,我会继续支持!
- 2023-01-04 09:54:21
-
- 如意的跳跳糖
- 这篇博文真及时,细节满满,赞 ??,收藏了,关注作者了!希望作者能多写Golang相关的文章。
- 2023-01-03 00:30:37
-
- 超级的热狗
- 这篇博文真是及时雨啊,太全面了,太给力了,收藏了,关注作者了!希望作者能多写Golang相关的文章。
- 2023-01-02 14:17:54
-
- 眼睛大的黄豆
- 太详细了,已加入收藏夹了,感谢大佬的这篇文章内容,我会继续支持!
- 2023-01-01 00:45:44
-
- 闪闪的狗
- 很有用,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,看完之后很有帮助,总算是懂了,感谢up主分享技术贴!
- 2022-12-29 01:53:54
-
- 尊敬的石头
- 写的不错,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,帮助很大,总算是懂了,感谢老哥分享文章!
- 2022-12-27 14:33:50
-
- 高贵的毛豆
- 太给力了,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,看完之后很有帮助,总算是懂了,感谢大佬分享博文!
- 2022-12-27 04:32:01
-
- 拉长的鲜花
- 感谢大佬分享,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,看完之后很有帮助,总算是懂了,感谢大佬分享技术文章!
- 2022-12-26 04:39:36
-
- 阳光的康乃馨
- 这篇博文出现的刚刚好,细节满满,受益颇多,码住,关注大佬了!希望大佬能多写Golang相关的文章。
- 2022-12-23 12:55:46
-
- Golang · Go教程 | 15小时前 | go标准库 · Go教程 · 字节处理 · 字节切片 Go 1.27 bytes.CutLast bytes.LastIndex 三返回值
- Go 1.27 bytes.CutLast 怎么封装:三返回值与 LastIndex 手写切片对比
- 276浏览 收藏
-
- Golang · Go教程 | 1天前 | 数字签名 · Go教程 · Go安全 · Go 1.27 crypto/ecdsa PrivateKey.Sign SignerOpts 哈希长度
- Go 1.27 ecdsa.PrivateKey.Sign 为什么检查哈希长度:SignerOpts 约束
- 212浏览 收藏
-
- Golang · Go教程 | 1天前 | 标准库 · Go教程 · 整数计算 · math/big RoundingMode Go 1.27 整数除法 Int.Divide
- Go 1.27 math/big.Int Divide 怎么选舍入:Trunc、Floor、Round 与 Ceil
- 202浏览 收藏
-
- Golang · Go教程 | 1天前 | HTTP服务 · Go教程 · 接口设计 · net/http Go 1.27 MaxHeaderValueCount MaxHeaderBytes HTTP安全
- Go 1.27 HTTP 请求头上限怎么设计:MaxHeaderValueCount 与 MaxHeaderBytes 配合
- 376浏览 收藏
-
- Golang · Go教程 | 1天前 | 标准库 · Go教程 · 工具开发 · 错误定位 · 语法分析 · Go 1.27 go/scanner Scanner.End token.Pos 语法诊断
- Go 1.27 go/scanner.Scanner.End 怎么定位 token 末端:起止位置与诊断范围
- 131浏览 收藏
-
- Golang · Go教程 | 1天前 | unsafe · Go教程 · Go升级 · go fix Go 1.27 unsafefuncs unsafe.Add
- Go 1.27 unsafefuncs 怎么改旧代码:函数指针转换的审查边界
- 368浏览 收藏
-
- Golang · Go教程 | 1天前 | go并发 · pprof · 故障排查 · Go教程 · 版本升级 · GODEBUG runtime/pprof Go 1.27 goroutine 标签 tracebacklabels
- Go 1.27 崩溃堆栈为什么多了 goroutine 标签:tracebacklabels 的取舍
- 174浏览 收藏
-
- Golang · Go教程 | 1天前 | 网络编程 · HTTP · go · 性能 · 连接复用 Go 1.27 http.Response.Body Response.Body.Close
- Go 1.27 http.Response.Body 关闭会自动排空什么:连接复用与异常边界
- 311浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- SuperCLUE
- SuperCLUE是权威的中文大语言模型综合评测基准,涵盖语言理解、知识应用、AI Agent智能体及安全性等12项核心能力。通过多轮对话与客观测试,定期发布榜单与技术报告,为模型研发、优化及行业选型提供科学依据。
- 100次使用
-
- C-Eval
- 深入了解C-Eval中文评估套件,涵盖52个学科与4级难度。本文详解其功能特点、Zero-shot/Few-shot使用方法及代码示例,助您全面评测LLM中文理解与泛化能力。
- 18次使用
-
- Gradio
- Gradio是一个用于构建机器学习和数据科学Web应用的开源Python库。支持快速创建交互界面,获Google、Meta等大厂青睐,适合模型演示、部署反馈及调试。
- 99次使用
-
- AutoGPT
- AutoGPT是基于GPT-4的开源AI代理平台,拥有超10万GitHub星标。本文介绍其低代码界面、自动化工作流功能、系统配置要求及安装步骤,助您高效部署和管理AI Agent。
- 104次使用
-
- 腾讯扣叮
- 腾讯扣叮是腾讯推出的6-18岁青少年编程学习平台,依托游戏与AI技术,提供图形化编程、3D创作、虚拟实验室及丰富赛事课程,助力培养计算思维与创新能力。
- 102次使用
-
- go高并发时append方法偶现错误解决分析
- 2022-12-22 188浏览
-
- Go中的错误和异常处理最佳实践方法
- 2023-01-09 100浏览
-
- Go语言异常处理(Panic和recovering)用法详解
- 2023-01-07 197浏览
-
- 一篇文章带你轻松搞懂Golang的error处理
- 2023-01-07 307浏览
-
- Go 处理大数组使用 for range 和 for 循环的区别
- 2023-01-09 272浏览

