V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
zzhirong
V2EX  ›  Go 编程语言

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

  •  
  •   zzhirong · 97 天前 · 2301 次点击
    这是一个创建于 97 天前的主题,其中的信息可能已经有所发展或是发生改变。

    起因是看到 https://ex.noerr.eu.org/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)。
    第 1 条附言  ·  96 天前
    可能有点幸存者偏差缘故(在 Go 区讨论 Go 语法), 一圈看下来, 好像对 Go 错误处理都还满意, 我看下来的结论就是, 显式错误处理喜欢的人也很多, 但, 一些高频用法(简单上抛错误)还是显啰嗦, 如果是处理稍复杂点的错误逻辑的话, `if err `方式还是挺好的。

    就不占用大家时间了, 帖子下沉了。
    Ayanokouji
        1
    Ayanokouji  
       97 天前
    我之前发的帖子,有不少评论的解决方案可以参考。
    /t/1101542
    PTLin
        2
    PTLin  
       97 天前   ❤️ 2
    你先把这一百多个被毙了的提案看一下吧,可以说 99%的人能想到的方法里面都有。
    https://github.com/golang/go/issues?q=label:error-handling
    zzhirong
        3
    zzhirong  
    OP
       97 天前
    @PTLin 其实我也没感觉 if err != nil { 有太大问题, 只不过语法上确实看起来有点冗余了点。
    Subilan
        5
    Subilan  
       97 天前 via iPhone
    我觉得最难顶的还是频繁多次错误处理的场景,err 变量不能重定义,要么事先专门声明一个,要么专门为每一个错误单独起个名字来定义。有更好的办法吗?
    vczyh
        6
    vczyh  
       97 天前
    就用 err.nn 吧,别的越用越麻烦,实在不行就换语言吧
    vczyh
        7
    vczyh  
       97 天前
    Subilan
        8
    Subilan  
       97 天前 via iPhone
    @vczyh 确实是一个方法,但是 if 可能会堆特别长,如果这个函数有其他数据返回,剩下的操作也只能在 if 里面做😐😐😐
    zzhirong
        9
    zzhirong  
    OP
       97 天前
    @pike0002 哈哈, 算个极端例子吧, 处理上抛这种情况确实不优雅了点。
    vczyh
        10
    vczyh  
       97 天前
    @Subilan 我一般这么写: https://imgur.com/a/x4qtu9A
    aloxaf
        11
    aloxaf  
       97 天前   ❤️ 1
    > 还有 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
        12
    lesismal  
       97 天前
    讲究工程性的工程师多数会觉得 if err 很好。
    我自己就是这种支持 if err 的,从没因为在 go 里写多几个 if err 感到任何不适,反而觉得这种强制或习惯让代码更健壮了、甚是欣慰。

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

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

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

    使用:

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

    go 的问题就是要写太多的 if err != nil , 如果能使用 check, try, ? 关键字或者符号,简化一下代码,那真的非常爽了
    chor02
        20
    chor02  
       97 天前
    我也不明白为什么不能有默认如果 err 不为空直接返回 err 的方案
    ltaoo1o
        21
    ltaoo1o  
       97 天前
    第一次接触到这种错误处理方案还是在 rust ,操作返回「结果」而不是「数据」,然后我就在 js 项目中引入了,配合 ts ,代码更健壮了

    ```js
    const r = await dosomething();
    if (r.error) {
    return;
    }
    // <-- 如果没有上面 if r.errror ,直接获取 r.data 在编辑器里面就会给出提示,r.data 可能是空
    console.log(r.data);
    ```
    LawlietZ
        22
    LawlietZ  
       97 天前
    这个问题讨论了八九年了吧 ?
    server
        23
    server  
       97 天前
    各有各的优势,各有各的问题,习惯就好了
    huigeer
        24
    huigeer  
       96 天前
    习惯就好,
    zzhirong
        25
    zzhirong  
    OP
       96 天前
    @LawlietZ 严格来说, 从 Go 正式发布开始就没停过吧, 后来伴随 Rust, TypeScript 等引入新的语法, 讨论又一次次开始, 我其实就是想看看这么多年过去了, 有无更优雅的方式出现。
    关于   ·   帮助文档   ·   自助推广系统   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2583 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 20ms · UTC 10:15 · PVG 18:15 · LAX 03:15 · JFK 06:15
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.