「GCTT 出品」Go 系列教程——30. 错误处理
connygpt 2024-09-25 15:46 5 浏览
Go语言中文网,致力于每日分享编码、开源等知识,欢迎关注我,会有意想不到的收获!
Go 系列教程是非常棒的一套初学者教程,入门就它了。
这是 Golang 系列教程中的第 30 篇。在本章教程中,我们将讨论 Go 语言中的错误处理。
什么是错误?
错误表示程序中出现了异常情况。比如当我们试图打开一个文件时,文件系统里却并没有这个文件。这就是异常情况,它用一个错误来表示。
在 Go 中,错误一直是很常见的。错误用内建的 error 类型来表示。
就像其他的内建类型(如 int、float64 等),错误值可以存储在变量里、作为函数的返回值等等。
示例
现在我们开始编写一个示例,该程序试图打开一个并不存在的文件。
在程序的第 9 行,我们试图打开路径为 /test.txt 的文件(playground 显然并不存在这个文件)。os 包里的 Open 函数有如下签名:
func Open(name string) (file *File, err error)
如果成功打开文件,Open 函数会返回一个文件句柄(File Handler)和一个值为 nil 的错误。而如果打开文件时发生了错误,会返回一个不等于 nil 的错误。
如果一个函数 或方法 返回了错误,按照惯例,错误会作为最后一个值返回。于是 Open 函数也是将 err 作为最后一个返回值。
按照 Go 的惯例,在处理错误时,通常都是将返回的错误与 nil 比较。nil 值表示了没有错误发生,而非 nil 值表示出现了错误。在这里,我们第 10 行检查了错误值是否为 nil。如果不是 nil,我们会简单地打印出错误,并在 main 函数中返回。
运行该程序会输出:
open /test.txt: No such file or directory
很棒!我们得到了一个错误,它指出该文件并不存在。
错误类型的表示
让我们进一步深入,理解 error 类型是如何定义的。error 是一个接口类型,定义如下:
error 有了一个签名为 Error() string 的方法。所有实现该接口的类型都可以当作一个错误类型。Error() 方法给出了错误的描述。
fmt.Println 在打印错误时,会在内部调用 Error() string 方法来得到该错误的描述。上一节示例中的第 11 行,就是这样打印出错误的描述的。
从错误获取更多信息的不同方法
现在,我们知道了 error 是一个接口类型,让我们看看如何从一个错误获取更多信息。
在前面的示例里,我们只是打印出错误的描述。如果我们想知道这个错误的文件路径,该怎么做呢?一种选择是直接解析错误的字符串。这是前面示例的输出:
open /test.txt: No such file or directory
我们解析了这条错误信息,虽然获取了发生错误的文件路径,但是这种方法很不优雅。随着语言版本的更新,这条错误的描述随时都有可能变化,使我们程序出错。
有没有更加可靠的方法来获取文件名呢?答案是肯定的,这是可以做到的,Go 标准库给出了各种提取错误相关信息的方法。我们一个个来看看吧。
1. 断言底层结构体类型,使用结构体字段获取更多信息
如果你仔细阅读了 Open 函数的文档,你可以看见它返回的错误类型是 *PathError。PathError 是结构体类型,它在标准库中的实现如下:
如果你有兴趣了解上述源代码出现的位置,可以在这里找到:https://golang.org/src/os/error.go?s=653:716#L11。
通过上面的代码,你就知道了 *PathError 通过声明 Error() string 方法,实现了 error 接口。Error() string 将文件操作、路径和实际错误拼接,并返回该字符串。于是我们得到该错误信息:
open /test.txt: No such file or directory
结构体 PathError 的 Path 字段,就有导致错误的文件路径。我们修改前面写的程序,打印出该路径。
在上面的程序里,我们在第 10 行使用了类型断言(Type Assertion)来获取 error 接口的底层值(Underlying Value)。接下来在第 11 行,我们使用 err.Path 来打印该路径。该程序会输出:
File at path /test.txt failed to open
很棒!我们已经使用类型断言成功获取到了该错误的文件路径。
2. 断言底层结构体类型,调用方法获取更多信息
第二种获取更多错误信息的方法,也是对底层类型进行断言,然后通过调用该结构体类型的方法,来获取更多的信息。
我们通过一个实例来理解这一点。
标准库中的 DNSError 结构体类型定义如下:
从上述代码可以看到,DNSError 结构体还有 Timeout() bool 和 Temporary() bool 两个方法,它们返回一个布尔值,指出该错误是由超时引起的,还是临时性错误。
接下来我们编写一个程序,断言 *DNSError 类型,并调用这些方法来确定该错误是临时性错误,还是由超时导致的。
在上述程序中,我们在第 9 行,试图获取 golangbot123.com(无效的域名) 的 ip。在第 10 行,我们通过 *net.DNSError 的类型断言,获取到了错误的底层值。接下来的第 11 行和第 13 行,我们分别检查了该错误是由超时引起的,还是一个临时性错误。
在本例中,我们的错误既不是临时性错误,也不是由超时引起的,因此该程序输出:
generic error: lookup golangbot123.com: no such host
如果该错误是临时性错误,或是由超时引发的,那么对应的 if 语句会执行,于是我们就可以适当地处理它们。
3. 直接比较
第三种获取错误的更多信息的方式,是与 error 类型的变量直接比较。我们通过一个示例来理解。
filepath 包中的 Glob 用于返回满足 glob 模式的所有文件名。如果模式写的不对,该函数会返回一个错误 ErrBadPattern。
filepath 包中的 ErrBadPattern 定义如下:
var ErrBadPattern = errors.New("syntax error in pattern")
errors.New() 用于创建一个新的错误。我们会在下一教程中详细讨论它。
当模式不正确时,Glob 函数会返回 ErrBadPattern。
我们来写一个小程序来看看这个错误。
在上述程序里,我们查询了模式为 [ 的文件,然而这个模式写的不正确。我们检查了该错误是否为 nil。为了获取该错误的更多信息,我们在第 10 行将 error 直接与 filepath.ErrBadPattern 相比较。如果该条件满足,那么该错误就是由模式错误导致的。该程序会输出:
syntax error in pattern
标准库在提供错误的详细信息时,使用到了上述提到的三种方法。在下一教程里,我们会通过这些方法来创建我们自己的自定义错误。
不可忽略错误
绝不要忽略错误。忽视错误会带来问题。接下来我重写上面的示例,在列出所有满足模式的文件名时,我省略了错误处理的代码。
我们已经从前面的示例知道了这个模式是错误的。在第 9 行,通过使用 _ 空白标识符,我忽略了 Glob 函数返回的错误。我在第 10 行简单打印了所有匹配的文件。该程序会输出:
matched files []
由于我忽略了错误,输出看起来就像是没有任何匹配了 glob 模式的文件,但实际上这是因为模式的写法不对。所以绝不要忽略错误。
本教程到此结束。
这一教程我们讨论了该如何处理程序中出现的错误,也讨论了如何查询关于错误的更多信息。简单概括一下本教程讨论的内容:
- 什么是错误?
- 错误的表示
- 获取错误详细信息的各种方法
- 不能忽视错误
在下一教程,我们会创建我们自己的自定义错误,并给标准错误增加更多的语境(Context)。
祝你愉快。
上一教程 - 「GCTT 出品」Go 系列教程——29. Defer
下一教程 - 自定义错误
历史文章:
「GCTT 出品」Go 系列教程——2. Hello World
「GCTT 出品」Go 系列教程——6. 函数(Function)
「GCTT 出品」Go 系列教程——10. switch 语句
「GCTT 出品」Go 系列教程——16. 结构体,这一篇就够
「GCTT 出品」Go 系列教程——22. 信道(channel)
相关推荐
- 3分钟让你的项目支持AI问答模块,完全开源!
-
hello,大家好,我是徐小夕。之前和大家分享了很多可视化,零代码和前端工程化的最佳实践,今天继续分享一下最近开源的Next-Admin的最新更新。最近对这个项目做了一些优化,并集成了大家比较关注...
- 干货|程序员的副业挂,12个平台分享
-
1、D2adminD2Admin是一个完全开源免费的企业中后台产品前端集成方案,使用最新的前端技术栈,小于60kb的本地首屏js加载,已经做好大部分项目前期准备工作,并且带有大量示例代码,助...
- Github标星超200K,这10个可视化面板你知道几个
-
在Github上有很多开源免费的后台控制面板可以选择,但是哪些才是最好、最受欢迎的可视化控制面板呢?今天就和大家推荐Github上10个好看又流行的可视化面板:1.AdminLTEAdminLTE是...
- 开箱即用的炫酷中后台前端开源框架第二篇
-
#头条创作挑战赛#1、SoybeanAdmin(1)介绍:SoybeanAdmin是一个基于Vue3、Vite3、TypeScript、NaiveUI、Pinia和UnoCSS的清新优...
- 搭建React+AntDeign的开发环境和框架
-
搭建React+AntDeign的开发环境和框架随着前端技术的不断发展,React和AntDesign已经成为越来越多Web应用程序的首选开发框架。React是一个用于构建用户界面的JavaScrip...
- 基于.NET 5实现的开源通用权限管理平台
-
??大家好,我是为广大程序员兄弟操碎了心的小编,每天推荐一个小工具/源码,装满你的收藏夹,每天分享一个小技巧,让你轻松节省开发效率,实现不加班不熬夜不掉头发,是我的目标!??今天小编推荐一款基于.NE...
- StreamPark - 大数据流计算引擎
-
使用Docker完成StreamPark的部署??1.基于h2和docker-compose进行StreamPark部署wgethttps://raw.githubusercontent.com/a...
- 教你使用UmiJS框架开发React
-
1、什么是Umi.js?umi,中文可发音为乌米,是一个可插拔的企业级react应用框架。你可以将它简单地理解为一个专注性能的类next.js前端框架,并通过约定、自动生成和解析代码等方式来辅助...
- 简单在线流程图工具在用例设计中的运用
-
敏捷模式下,测试团队的用例逐渐简化以适应快速的发版节奏,大家很早就开始运用思维导图工具比如xmind来编写测试方法、测试点。如今不少已经不少利用开源的思维导图组件(如百度脑图...)来构建测试测试...
- 【开源分享】神奇的大数据实时平台框架,让Flink&Spark开发更简单
-
这是一个神奇的框架,让Flink|Spark开发更简单,一站式大数据实时平台!他就是StreamX!什么是StreamX大数据技术如今发展的如火如荼,已经呈现百花齐放欣欣向荣的景象,实时处理流域...
- 聊聊规则引擎的调研及实现全过程
-
摘要本期主要以规则引擎业务实现为例,陈述在陌生业务前如何进行业务深入、调研、技术选型、设计及实现全过程分析,如果你对规则引擎不感冒、也可以从中了解一些抽象实现过程。诉求从硬件采集到的数据提供的形式多种...
- 【开源推荐】Diboot 2.0.5 发布,自动化开发助理
-
一、前言Diboot2.0.5版本已于近日发布,在此次发布中,我们新增了file-starter组件,完善了iam-starter组件,对core核心进行了相关优化,让devtools也支持对IAM...
- 微软推出Copilot Actions,使用人工智能自动执行重复性任务
-
IT之家11月19日消息,微软在今天举办的Ignite大会上宣布了一系列新功能,旨在进一步提升Microsoft365Copilot的智能化水平。其中最引人注目的是Copilot...
- Electron 使用Selenium和WebDriver
-
本节我们来学习如何在Electron下使用Selenium和WebDriver。SeleniumSelenium是ThoughtWorks提供的一个强大的基于浏览器的开源自动化测试工具...
- Quick 'n Easy Web Builder 11.1.0设计和构建功能齐全的网页的工具
-
一个实用而有效的应用程序,能够让您轻松构建、创建和设计个人的HTML网站。Quick'nEasyWebBuilder是一款全面且轻巧的软件,为用户提供了一种简单的方式来创建、编辑...
- 一周热门
- 最近发表
- 标签列表
-
- kubectlsetimage (56)
- mysqlinsertoverwrite (53)
- addcolumn (54)
- helmpackage (54)
- varchar最长多少 (61)
- 类型断言 (53)
- protoc安装 (56)
- jdk20安装教程 (60)
- rpm2cpio (52)
- 控制台打印 (63)
- 401unauthorized (51)
- vuexstore (68)
- druiddatasource (60)
- 企业微信开发文档 (51)
- rendertexture (51)
- speedphp (52)
- gitcommit-am (68)
- bashecho (64)
- str_to_date函数 (58)
- yum下载包及依赖到本地 (72)
- jstree中文api文档 (59)
- mvnw文件 (58)
- rancher安装 (63)
- nginx开机自启 (53)
- .netcore教程 (53)