Go 语言核心 36 讲
郝林
《Go 并发编程实战》作者,前轻松筹大数据负责人
79610 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 55 讲
Go 语言核心 36 讲
15
15
1.0x
00:00/00:00
登录|注册

20 | 错误处理 (下)

net.UnknownNetworkError
*net.AddrError
*net.OpError
最小化访问权限
常量化
私有化变量
可能被篡改
Err字段
具体错误类型
Temporary方法
Timeout方法
链式错误关联
解决方案
隐患
错误值关联
net.Error接口
访问权限最小化
私有化变量
常量化
错误关联
错误类型树
公开变量
errors.New函数
链式关系
立体错误类型体系
包含的错误种类
扁平错误值列表
立体错误类型体系
扁平错误值列表
错误值类型
错误值列表
错误体系设计
处理错误值
经常用到的错误值
建造者角度
使用者角度
思考题
错误处理
Go语言错误处理

该思维导图由 AI 生成,仅供参考

你好,我是郝林,今天我们继续来分享错误处理。
在上一篇文章中,我们主要讨论的是从使用者的角度看“怎样处理好错误值”。那么,接下来我们需要关注的,就是站在建造者的角度,去关心“怎样才能给予使用者恰当的错误值”的问题了。

知识扩展

问题:怎样根据实际情况给予恰当的错误值?
我们已经知道,构建错误值体系的基本方式有两种,即:创建立体的错误类型体系和创建扁平的错误值列表。
先说错误类型体系。由于在 Go 语言中实现接口是非侵入式的,所以我们可以做得很灵活。比如,在标准库的net代码包中,有一个名为Error的接口类型。它算是内建接口类型error的一个扩展接口,因为errornet.Error的嵌入接口。
net.Error接口除了拥有error接口的Error方法之外,还有两个自己声明的方法:TimeoutTemporary
net包中有很多错误类型都实现了net.Error接口,比如:
*net.OpError
*net.AddrError
net.UnknownNetworkError等等。
你可以把这些错误类型想象成一棵树,内建接口error就是树的根,而net.Error接口就是一个在根上延伸的第一级非叶子节点。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

Go语言错误处理(下)文章主要从两个视角总结了错误类型、错误值的处理技巧和设计方式。首先介绍了构建错误值体系的基本方式,包括创建立体的错误类型体系和创建扁平的错误值列表。在讨论错误类型体系时,以`net`包中的`Error`接口类型为例,说明了如何通过接口嵌套和实现构建错误类型树,并介绍了链式错误关联的设计。另外,对于扁平的错误值列表,提出了让错误值成为常量或编写私有的错误值以及公开的获取和判等函数的解决方案。总结时强调了“最小化访问权限”这一程序设计原则的重要性。 文章内容深入浅出,通过具体的示例和技术原理,帮助读者理解了Go语言中错误处理的技术特点和设计思路。读者可以从中了解到如何根据实际情况给予恰当的错误值,以及在不同情况下选择合适的错误类型体系或错误值列表。文章内容丰富,对于Go语言开发者来说具有一定的参考价值。 总体而言,本文通过深入的技术讨论,为读者提供了关于Go语言错误处理的全面概览,使读者能够快速了解错误处理的基本方式和设计原则。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Go 语言核心 36 讲》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(16)

  • 最新
  • 精选
  • 罗峰
    os.ErrClosed 这个包外可访问变量,居然可以修改,这个是个bug吗?

    作者回复: 这严格来说不是bug,这是Go语言原生的编程风格决定的。但我个人认为这是一个很不好的风格。 我刚刚又看了一下 Go 1.16 的源码(如 os 包、io/fs 包、internal/oserror 包、net 包、internal/poll 包等)。 我估计Go语言官方也早已意识到了这个问题,他们已经做了一些改进。大家如果有兴趣可以去看看。鉴于篇幅限制,我在这里只说 os.ErrClosed 吧。 这个 os.ErrClosed 的声明代码如下: var ErrClosed = fs.ErrClosed // "file already closed" 其中的 fs 指代的是 io/fs 包,该包有代码: var ErrClosed = errClosed() // "file already closed" func errClosed() error { return oserror.ErrClosed } 其中的 oserror 指代的是 internal/oserror.ErrClosed 包,该包中有代码: var ErrClosed = errors.New("file already closed") 也就是说,变量 os.ErrClosed 最终指向的是一个 internal(只有Go标准库的内部代码才能访问到的)值,即 internal/oserror.ErrClosed 变量的值 errors.New("file already closed") 。 这里为什么要绕这么一大圈呢??实际上,为了保证Go语言标准库的向后兼容性,目前已经存在的这类原生风格的代码在当前是没办法完全修正的。例如,os.ErrClosed 的名字没法改变,一旦改变就破坏了向后兼容性,同时也没法把 os.ErrClosed 这个标识符指代的程序实体由变量改为函数,一旦改了照样破坏向后兼容性。在我看来,这着实有些尴尬。 既然解决不了问题,那Go语言官方为什么还要做出上述的改进呢??我估计,这是在为以后的大改做准备。你看,一旦把暴露在外的 error 类型变量(如 os.ErrClosed)的初识值改为对一个 internal(只可内部访问的)值的引用,就完全可以在Go标准库的内部来判断该变量的值是否“依然是初始值”了。如果遇到恶意/意外修改,那么就有能力判断和恢复了。 虽然我还没在Go标准库的源码中看到这样的判断(也许是有的,然而我至此还没看到),但这终归是为进一步优化留了重要的口子。 不过,遗憾的是,由于它们最终引用到的是 internal 包中的变量,所以(在Go标准库外部的)我们的代码依然没有能力做上述判断和恢复。这依然有些尴尬。 所以,我在此呼吁大家,千万不要在自己的项目中遵从这样的原生风格(也就是,把错误变量的名称首字母大写)。我们可以这样做(参考代码如下): var errClosed = errors.New("file already closed") // 变量名首字母小写,以防止来自包外的修改。 func ErrClosed() error { // 函数名首字母大写,以便来自包外的访问。 return errClosed // 总是返回同一个内部的错误值。 } 这样就可以完美避免掉包外代码对原有错误值的恶意/意外修改了,同时还不会影响到错误值的获取和判断。 或者,如果想以(包含了多个代码包的)某个模块为单位,那么可以把这类代表错误的变量都声明到这个模块的 internal 包中去,就像Go语言标准库做的那样。 好了,说了这么多,希望我说明白了。如果还有疑问,可以继续留言给我。

    2021-06-06
    21
  • 忘怀
    讲得很好,建议配一些图,用大量文字不易说明。

    作者回复: 等稿子都赶完了,我会集中精力补图的。

    2018-09-26
    12
    16
  • honnkyou
    结构体中的接口字段怎么理解?比如error,是可以等价成实现error接口的结构体是吗?

    作者回复: 意思是,这个字段可以容纳的值的类型是一个范围,而不是一个明确的类型。这就相当于给这个结构体的部分实现创造了动态替换的条件。 如果这个接口类型的结构体是匿名嵌入的,那么基本上是可以等价的。当然了,这种情况下,要保证这个字段是有值的,否则真正调用的时候会报错。

    2020-05-29
    4
  • Tianz
    也非常期望在说到标准库里怎么使用 error 的时候,直接贴出一点它的代码,酱紫就秒懂了一些(当然自己不懒可以去源码里搜刮啦)

    作者回复: 建议不要犯懒;) 光听我说没有太大意义。

    2020-06-07
    2
    2
  • Dylan
    第二遍回来看,比以前清晰好多,但如果有图的话我觉得这门课会更上一层楼

    作者回复: 好,下回再改进吧:)

    2020-10-30
  • ken
    老师您好,麻烦有空也把您留的作业题目 给下标准答案吧。不然像我这样的小白。看留言都不知道那个答案是对的。非常期待。另外如何加入微信群呢?
    2018-10-04
    37
  • 猫王者
    看完这两章的错误处理,有个疑问,为什么在程序中需要知道错误的类型呢,一般程序出错,我直接打印err变量到日志不就好了,管你什么类型,都是有字符串输出的吧,我吧这些字符串输出到日志就完事了,所以获取这些错误的具体类型的意义是什么呢
    2018-10-17
    4
    21
  • yandongxiao
    最小化访问权限 和 链式错误处理,学到了。
    2018-09-27
    12
  • 小韩
    书读百遍其义自见,专栏也需走两遍。
    2019-08-27
    6
  • 先听
    "用类型建立起树形结构的错误体系,用统一字段建立起可追根溯源的链式错误关联"-这听起来很像Java等语言里面自带的错误处理机制里面的功能啊。go语言舍弃了这些,而我们又要手动去实现这些,这到底是为什么呢?... 想不明白,内心好纠结...
    2018-12-16
    2
    3
收起评论
显示
设置
留言
16
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部