Golang中重复错误处理的优化方法
在Golang实战开发的过程中,我们经常会遇到一些这样那样的问题,然后要卡好半天,等问题解决了才发现原来一些细节知识点还是没有掌握好。今天golang学习网就整理分享《Golang中重复错误处理的优化方法》,聊聊错误、优化,希望可以帮助到正在努力赚钱的你。
Golang 错误处理最让人头疼的问题就是代码里充斥着「if err != nil」,它们破坏了代码的可读性,本文收集了几个例子,让大家明白如何优化此类问题。
让我们看看 Errors are values 中提到的一个 io.Writer 例子:
_, err = fd.Write(p0[a:b])
if err != nil {
return err
}
_, err = fd.Write(p1[c:d])
if err != nil {
return err
}
_, err = fd.Write(p2[e:f])
if err != nil {
return err
}
如上代码乍一看无法直观的看出其本来的意图是什么,改进版:
type errWriter struct {
w io.Writer
err error
}
func (ew *errWriter) write(buf []byte) {
if ew.err != nil {
return
}
_, ew.err = ew.w.Write(buf)
}
ew := &errWriter{w: fd}
ew.write(p0[a:b])
ew.write(p1[c:d])
ew.write(p2[e:f])
if ew.err != nil {
return ew.err
}
通过自定义类型 errWriter 来封装 io.Writer,并且封装了 error,新类型有一个 write 方法,不过其方法签名并没有返回 error,而是在方法内部判断一旦有问题就立刻返回,有了这些准备工作,我们就可以把原本穿插在业务逻辑中间的错误判断提出来放到最后来统一调用,从而在视觉上保证让人可以直观的看出代码本来的意图是什么。
让我们再看看 Eliminate error handling by eliminating errors 中提到的另一个 io.Writer 例子:
type Header struct {
Key, Value string
}
type Status struct {
Code int
Reason string
}
func WriteResponse(w io.Writer, st Status, headers []Header, body io.Reader) error {
_, err := fmt.Fprintf(w, "HTTP/1.1 %d %s\r\n", st.Code, st.Reason)
if err != nil {
return err
}
for _, h := range headers {
_, err := fmt.Fprintf(w, "%s: %s\r\n", h.Key, h.Value)
if err != nil {
return err
}
}
if _, err := fmt.Fprint(w, "\r\n"); err != nil {
return err
}
_, err = io.Copy(w, body)
return err
}
第一感觉既然错误是 fmt.Fprint 和 io.Copy 返回的,是不是我们要重新封装一下它们?实际上真正的源头是它们的参数 io.Writer,因为直接调用 io.Writer 的 Writer 方法的话,方法签名中有返回值 error,所以每一步 fmt.Fprint 和 io.Copy 操作都不得不进行重复的错误处理,看上去是坏味道,改进版:
type errWriter struct {
io.Writer
err error
}
func (e *errWriter) Write(buf []byte) (int, error) {
if e.err != nil {
return 0, e.err
}
var n int
n, e.err = e.Writer.Write(buf)
return n, nil
}
func WriteResponse(w io.Writer, st Status, headers []Header, body io.Reader) error {
ew := &errWriter{Writer: w}
fmt.Fprintf(ew, "HTTP/1.1 %d %s\r\n", st.Code, st.Reason)
for _, h := range headers {
fmt.Fprintf(ew, "%s: %s\r\n", h.Key, h.Value)
}
fmt.Fprint(ew, "\r\n")
io.Copy(ew, body)
return ew.err
}
通过自定义类型 errWriter 来封装 io.Writer,并且封装了 error,同时重写了 Writer 方法,虽然方法签名中仍然有返回值 error,但是我们单独保存了一份 error,并且在方法内部判断一旦有问题就立刻返回,有了这些准备工作,新版的 WriteResponse 不再有重复的错误判断,只需要在最后检查一下 error 即可。
类似的做法在 Golang 标准库中屡见不鲜,让我们继续看看 Eliminate error handling by eliminating errors 中提到的一个关于 bufio.Reader 和 bufio.Scanner 的例子:
func CountLines(r io.Reader) (int, error) {
var (
br = bufio.NewReader(r)
lines int
err error
)
for {
_, err = br.ReadString('\n')
lines++
if err != nil {
break
}
}
if err != io.EOF {
return 0, err
}
return lines, nil
}
我们构造一个 bufio.Reader,然后在一个循环中调用 ReadString 方法,如果读到文件结尾,那么 ReadString 会返回一个错误(io.EOF),为了判断此类情况,我们不得不在每次循环时判断「if err != nil」,看上去这是坏味道,改进版:
func CountLines(r io.Reader) (int, error) {
sc := bufio.NewScanner(r)
lines := 0
for sc.Scan() {
lines++
}
return lines, sc.Err()
}
实际上,和 bufio.Reader 相比,bufio.Scanner 是一个更高阶的类型,换句话简单点来说的话,相当于是 bufio.Scanner 抽象了 bufio.Reader,通过把低阶的 bufio.Reader 换成高阶的 bufio.Scanner,循环中不再需要判断「if err != nil」,因为 Scan 方法签名不再返回 error,而是返回 bool,当在循环里读到了文件结尾的时候,循环直接结束,如此一来,我们就可以统一在最后调用 Err 方法来判断成功还是失败,看看 Scanner 的定义:
type Scanner struct {
r io.Reader // The reader provided by the client.
split SplitFunc // The function to split the tokens.
maxTokenSize int // Maximum size of a token; modified by tests.
token []byte // Last token returned by split.
buf []byte // Buffer used as argument to split.
start int // First non-processed byte in buf.
end int // End of data in buf.
err error // Sticky error.
empties int // Count of successive empty tokens.
scanCalled bool // Scan has been called; buffer is in use.
done bool // Scan has finished.
}
可见 Scanner 封装了 io.Reader,并且封装了 error,和我们之前讨论的做法一致。有一点说明一下,实际上查看 Scan 源代码的话,你会发现它不是通过 err 来判断是否结束的,而是通过 done 来判断是否结束,这是因为 Scan 只有遇到文件结束的错误才退出,其它错误会继续执行,当然,这只是具体的细节问题,不影响我们的结论。
通过对以上几个例子的分析,我们可以得出优化重复错误处理的大概套路:通过创建新的类型来封装原本干脏活累活的旧类型,同时在新类型中封装 error,新旧类型的方法签名可以保持兼容,也可以不兼容,这个不是关键的,视客观情况而定,至于具体的逻辑实现,先判断有没有 error,如果有就直接退出,如果没有就继续执行,并且在执行过程中保存可能出现的 error 以便后面操作使用,最后通过统一调用新类型的 error 来完成错误处理。提醒一下,此方案的缺点是要到最后才能知道有没有错误,好在如此的控制粒度在多数时候并无大碍。
总结
文中关于golang的知识介绍,希望对你的学习有所帮助!若是受益匪浅,那就动动鼠标收藏这篇《Golang中重复错误处理的优化方法》文章吧,也可关注golang学习网公众号了解相关技术文章。
详解golang碎片整理之 fmt.Scan
- 上一篇
- 详解golang碎片整理之 fmt.Scan
- 下一篇
- go for range坑和闭包坑的分析
-
- Golang · Go教程 | 6小时前 | 格式化输出 printf fmt库 格式化动词 Stringer接口
- Golangfmt库用法与格式化技巧解析
- 140浏览 收藏
-
- Golang · Go教程 | 6小时前 |
- Golang配置Protobuf安装教程
- 147浏览 收藏
-
- Golang · Go教程 | 6小时前 |
- Golang中介者模式实现与通信解耦技巧
- 378浏览 收藏
-
- Golang · Go教程 | 7小时前 |
- Golang多协程通信技巧分享
- 255浏览 收藏
-
- Golang · Go教程 | 7小时前 |
- Golang如何判断变量类型?
- 393浏览 收藏
-
- Golang · Go教程 | 7小时前 |
- Golang云原生微服务实战教程
- 310浏览 收藏
-
- Golang · Go教程 | 8小时前 |
- Golang迭代器与懒加载结合应用
- 110浏览 收藏
-
- Golang · Go教程 | 8小时前 | 性能优化 并发安全 Golangslicemap 预设容量 指针拷贝
- Golangslicemap优化技巧分享
- 412浏览 收藏
-
- Golang · Go教程 | 8小时前 |
- Golang代理模式与访问控制实现解析
- 423浏览 收藏
-
- Golang · Go教程 | 8小时前 |
- Golang事件管理模块实现教程
- 274浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- ChatExcel酷表
- ChatExcel酷表是由北京大学团队打造的Excel聊天机器人,用自然语言操控表格,简化数据处理,告别繁琐操作,提升工作效率!适用于学生、上班族及政府人员。
- 3166次使用
-
- Any绘本
- 探索Any绘本(anypicturebook.com/zh),一款开源免费的AI绘本创作工具,基于Google Gemini与Flux AI模型,让您轻松创作个性化绘本。适用于家庭、教育、创作等多种场景,零门槛,高自由度,技术透明,本地可控。
- 3379次使用
-
- 可赞AI
- 可赞AI,AI驱动的办公可视化智能工具,助您轻松实现文本与可视化元素高效转化。无论是智能文档生成、多格式文本解析,还是一键生成专业图表、脑图、知识卡片,可赞AI都能让信息处理更清晰高效。覆盖数据汇报、会议纪要、内容营销等全场景,大幅提升办公效率,降低专业门槛,是您提升工作效率的得力助手。
- 3408次使用
-
- 星月写作
- 星月写作是国内首款聚焦中文网络小说创作的AI辅助工具,解决网文作者从构思到变现的全流程痛点。AI扫榜、专属模板、全链路适配,助力新人快速上手,资深作者效率倍增。
- 4512次使用
-
- MagicLight
- MagicLight.ai是全球首款叙事驱动型AI动画视频创作平台,专注于解决从故事想法到完整动画的全流程痛点。它通过自研AI模型,保障角色、风格、场景高度一致性,让零动画经验者也能高效产出专业级叙事内容。广泛适用于独立创作者、动画工作室、教育机构及企业营销,助您轻松实现创意落地与商业化。
- 3788次使用
-
- go高并发时append方法偶现错误解决分析
- 2022-12-22 188浏览
-
- Golang中的错误处理的示例详解
- 2022-12-22 119浏览
-
- Go中的错误和异常处理最佳实践方法
- 2023-01-09 100浏览
-
- 分析Go错误处理优化go recover机制缺陷
- 2023-01-01 483浏览
-
- 一篇文章带你轻松搞懂Golang的error处理
- 2023-01-07 307浏览

