type
status
date
slug
summary
tags
category
icon
password

Error 和 Exception

如果一个函数返回了(value, error), 你不能对value做任何假设,必须先判定error, 唯一可以忽略error的是, value本身就不需要关心
 
慎用panic: 仅用于不可恢复的程序错误,例如数组越界,不可恢复等问题
Error 鼓励及时处理
Error are values
 

Sentinel Error

预定义的特定错误;
使用方法: if err == ErrObj { … }
使用 sentinel 值是最不灵活的错误处理策略,因为调用方必须使用 == 将结果与预先声明的值进行比较。当您想要提供更多的上下文时,这就出现了一个问题,因为返回一个不同的错误将破坏相等性检查。
甚至是一些有意义的 fmt.Errorf 携带一些上下文,也会破坏调用者的 == ,调用者将被迫查看 error.Error() 方法的输出,以查看它是否与特定的字符串匹配。
 
  • 不依赖检查 error.Error 的输出。
    • 不应该依赖检测 error.Error 的输出,Error 方法存在于 error 接口主要用于方便程序员使用,但不是程序(编写测试可能会依赖这个返回)。这个输出的字符串用于记录日志、输出到 stdout 等。
 
  • Sentinel errors 成为你 API 公共部分。
    • 如果您的公共函数或方法返回一个特定值的错误,那么该值必须是公共的,当然要有文档记录,这会增加 API 的表面积。
      如果 API 定义了一个返回特定错误的 interface,则该接口的所有实现都将被限制为仅返回该错误,即使它们可以提供更具描述性的错误。
      比如 io.Reader。像 io.Copy 这类函数需要 reader 的实现者比如返回 io.EOF 来告诉调用者没有更多数据了,但这又不是错误。
       
  • Sentinel errors 在两个包之间创建了依赖。
    • sentinel errors 最糟糕的问题是它们在两个包之间创建了源代码依赖关系。例如,检查错误是否等于 io.EOF,您的代码必须导入 io 包。这个特定的例子听起来并不那么糟糕,因为它非常常见,但是想象一下,当项目中的许多包导出错误值时,存在耦合,项目中的其他包必须导入这些错误值才能检查特定的错误条件(in the form of an import loop)。
      包级别的依赖当到一定程度时,很容易形成循环依赖, 这在go中是不允许的
       
  • 结论: 尽可能避免 sentinel errors。
我的建议是避免在编写的代码中使用 sentinel errors。在标准库中有一些使用它们的情况,但这不是一个您应该模仿的模式。
 

Error types

实现了 error接口的自定义类型
与错误值相比,错误类型的一大改进是它们能够包装底层错误以提供更多上下文。
一个不错的例子就是 os.PathError  它提供了底层执行了什么操作、那个路径出了什么问题。
调用者要使用类型断言和类型 switch,就要让自定义的 error 变为 public。这种模型会导致和调用者产生强耦合,从而导致 API 变得脆弱。
结论是尽量避免使用 error types,虽然错误类型比 sentinel errors 更好,因为它们可以捕获关于出错的更多上下文,但是 error types 共享 error values 许多相同的问题。
因此,我的建议是避免错误类型,或者至少避免将它们作为公共 API 的一部分。

Opaque Error

因为它要求代码和调用者之间的耦合最少。
我将这种风格称为不透明错误处理,因为虽然您知道发生了错误,但您没有能力看到错误的内部。作为调用者,关于操作的结果,您所知道的就是它起作用了,或者没有起作用(成功还是失败)。
这就是不透明错误处理的全部功能–只需返回错误而不假设其内容。
  • Assert errors for behaviour, not type
在少数情况下,这种二分错误处理方法是不够的。例如,与进程外的世界进行交互(如网络活动),需要调用方调查错误的性质,以确定重试该操作是否合理。在这种情况下,我们可以断言错误实现了特定的行为,而不是断言错误是特定的类型或值。
通过定义一个error对象实现某个接口, 正常情况我们只需要根据二分错误的方式来进行处理业务; 当需要获取错误详情的时候, 可以通过类型断言的方式来通过指定接口获取对应信息;
这是比较推荐的方式
 

Handing Error

无错误的正常流程代码,将成为一条直线,而不是缩进的代码。
Pkg/errors:
Errors.Wrap(err, "xxx")  ---  保留根因err, 并保留堆栈信息
Errors.WithMessage(err, "")  ---  不保存堆栈信息
基础库不应该使用wrap的方式来保存堆栈, 应该通过预定义错误的方式来进行返回;
当wrap后,如果通过WithMessage来补充信息, 信息会显示在堆栈之后; (不建议这样写)
当收到一个错误时, 如果不是一个预定义的错误, 不应该再使用 pkg/errors 去包装它;
如果一个错误已经被处理, 就不应该再往上抛 (当收到一个错误时, 要么处理, 要么往上抛, 不建议混用)
 
并行编程微服务概述
Loading...
leiax00
leiax00
让每一天都有意义🍚
最新发布
异地组网-zerotier
2025-4-10
apt/snap常用操作
2025-4-7
ubuntu操作备忘录
2025-4-1
git常用操作
2025-4-1
小智聊天机器人使用记录
2025-3-31
k8s/docker常用操作
2025-3-26
公告