探讨一下错误处理策略, 关于 if err != nil {

148 天前
 zzhirong

起因是看到 https://v2ex.com/t/1128449?p=1#reply41 写了

"相比之下 if err != nil 95%的时候都很不优雅"

好像我也看到很多吐槽 Go 错误处理语法的, 以下错误处理策略摘录自<<The Go Programming Language>> :

  1. Propagate the error. # 上抛
  2. Retry the failed operation. # 重试
  3. If progress is impossible, the caller can print the error and stop the program gracefully. # 严重错误, 退出程序
  4. It’s sufficient just to log the error and then continue, perhaps with reduced functionality. # 打印错误, 服务降级运行
  5. In rare cases we can safely ignore an error entirely. # 忽略错误

有无更优雅的方式能够实现上面的处理策略?

已知的两种:

  1. try except finally 除了实现 1 (上抛)优雅点, 剩下的也没看出多大优势,而且错误发生点和处理点不在同一处,这就注定需要额外的代码来重构上下文, 比如,打开 file1, 然后 file2, 但在打开第 file2 时发生错误,处理的时候得先判定是打开哪个文件出错了,再执行相应错误处理和回滚操作。
  2. 还有 Optional 一类的,和 if 没太大区别,只不过前者在语法上做了强制(不检查错误,不能拿到结果),还有一些语法糖( user?.address)。
2443 次点击
所在节点    Go 编程语言
25 条回复
Ayanokouji
148 天前
我之前发的帖子,有不少评论的解决方案可以参考。
/t/1101542
PTLin
148 天前
你先把这一百多个被毙了的提案看一下吧,可以说 99%的人能想到的方法里面都有。
https://github.com/golang/go/issues?q=label:error-handling
zzhirong
148 天前
@PTLin 其实我也没感觉 if err != nil { 有太大问题, 只不过语法上确实看起来有点冗余了点。
pike0002
148 天前
Subilan
148 天前
我觉得最难顶的还是频繁多次错误处理的场景,err 变量不能重定义,要么事先专门声明一个,要么专门为每一个错误单独起个名字来定义。有更好的办法吗?
vczyh
148 天前
就用 err.nn 吧,别的越用越麻烦,实在不行就换语言吧
vczyh
148 天前
Subilan
148 天前
@vczyh 确实是一个方法,但是 if 可能会堆特别长,如果这个函数有其他数据返回,剩下的操作也只能在 if 里面做😐😐😐
zzhirong
148 天前
@pike0002 哈哈, 算个极端例子吧, 处理上抛这种情况确实不优雅了点。
vczyh
148 天前
@Subilan 我一般这么写: https://imgur.com/a/x4qtu9A
aloxaf
148 天前
> 还有 Optional 一类的,和 if 没太大区别,只不过前者在语法上做了强制(不检查错误,不能拿到结果),还有一些语法糖( user?.address)。

大部分人想要的不就是糖吗?谁会关心和类型和积类型的区别,自己写得爽就行了。
即使 Optional 这类方案,没有糖也很难受,比 if err != nil 好看不到哪儿去。

比如 Rust 没有任何糖的上抛:
let ret = match foo() {
Ok(v) => v,
err => return err,
}

后面大家受不了,加入了 try! 宏:
let ret = try!(foo());

再后面直接加入了问号运算符:
let ret = foo()?;
lesismal
148 天前
讲究工程性的工程师多数会觉得 if err 很好。
我自己就是这种支持 if err 的,从没因为在 go 里写多几个 if err 感到任何不适,反而觉得这种强制或习惯让代码更健壮了、甚是欣慰。

引用一段别人文章里总结的 Rob Pike 老爷子内容:
显式错误处理:if err != nil 的模式强制开发者在调用点处理错误,使得问题更难被“隐藏”到上层去统一“包装”处理,鼓励在错误发生的源头附近解决或添加上下文。

完整文章:
https://tonybai.com/2025/04/27/rob-pike-on-bloat/
zzhirong
148 天前
@lesismal 我也认为显式比隐式更好, 目前吐槽最多的应该是, 显式上抛以及层层上抛(导致了很多重复的代码), 在翻看 #2 给出的链接中的提案时, 发现一句话, "The goal is not to replace all error handling. The goal is to replace the most common case", 我也很认可, 针对高频的用法做些优化, 也未尝不可。
log4j
148 天前
习惯了表示还行,显示处理 error 工程上来讲还是很好的
Subilan
148 天前
@vczyh #10 我得感谢 Go 没有禁止这种情况下的 err 重定义(但会被 IDE 画上线)
zzhirong
148 天前
@log4j 确实, 对代码阅读也很友好, 相对于, 一个小小的 ? 就可能是一个函数出口来说, 好多了。
Hopetree
148 天前
我现在写 Python 还经常借鉴 Go 的这种显示的异常语法,Go 又不是强制让你这么写,你要是不想判断你就直接异常不就行了
henix
148 天前
我比较喜欢 Go 这种错误处理方式,它的问题在于代码啰嗦,而且没有宏,而且按照 Go 语言的设计哲学,基本上不可能加入宏的
我自己的 C 语言基础库的错误处理就参考了 Go ,而且通过宏来减少重复代码,例如:

#define E(s) { err_t err = s; if (err != NULL) return err; } (void)0

使用:

E(do_some_thing(a));
bruce0
148 天前
go 通过返回值判断错误的方式我觉得挺好的,现在写 CPP 很多我都这样写,用 pair 或者 tuple, cpp23 有了 expected,

go 的问题就是要写太多的 if err != nil , 如果能使用 check, try, ? 关键字或者符号,简化一下代码,那真的非常爽了
chor02
148 天前
我也不明白为什么不能有默认如果 err 不为空直接返回 err 的方案

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1128629

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX