20 | 错误处理 (下)
该思维导图由 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-0621 - 忘怀讲得很好,建议配一些图,用大量文字不易说明。
作者回复: 等稿子都赶完了,我会集中精力补图的。
2018-09-261216 - honnkyou结构体中的接口字段怎么理解?比如error,是可以等价成实现error接口的结构体是吗?
作者回复: 意思是,这个字段可以容纳的值的类型是一个范围,而不是一个明确的类型。这就相当于给这个结构体的部分实现创造了动态替换的条件。 如果这个接口类型的结构体是匿名嵌入的,那么基本上是可以等价的。当然了,这种情况下,要保证这个字段是有值的,否则真正调用的时候会报错。
2020-05-294 - Tianz也非常期望在说到标准库里怎么使用 error 的时候,直接贴出一点它的代码,酱紫就秒懂了一些(当然自己不懒可以去源码里搜刮啦)
作者回复: 建议不要犯懒;) 光听我说没有太大意义。
2020-06-0722 - Dylan第二遍回来看,比以前清晰好多,但如果有图的话我觉得这门课会更上一层楼
作者回复: 好,下回再改进吧:)
2020-10-30 - ken老师您好,麻烦有空也把您留的作业题目 给下标准答案吧。不然像我这样的小白。看留言都不知道那个答案是对的。非常期待。另外如何加入微信群呢?2018-10-0437
- 猫王者看完这两章的错误处理,有个疑问,为什么在程序中需要知道错误的类型呢,一般程序出错,我直接打印err变量到日志不就好了,管你什么类型,都是有字符串输出的吧,我吧这些字符串输出到日志就完事了,所以获取这些错误的具体类型的意义是什么呢2018-10-17421
- yandongxiao最小化访问权限 和 链式错误处理,学到了。2018-09-2712
- 小韩书读百遍其义自见,专栏也需走两遍。2019-08-276
- 先听"用类型建立起树形结构的错误体系,用统一字段建立起可追根溯源的链式错误关联"-这听起来很像Java等语言里面自带的错误处理机制里面的功能啊。go语言舍弃了这些,而我们又要手动去实现这些,这到底是为什么呢?... 想不明白,内心好纠结...2018-12-1623