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 · 2025 年 4 月 28 日 · 2780 次点击
    这是一个创建于 257 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    就不占用大家时间了, 帖子下沉了。
    Ayanokouji
        1
    Ayanokouji  
       2025 年 4 月 28 日
    我之前发的帖子,有不少评论的解决方案可以参考。
    /t/1101542
    PTLin
        2
    PTLin  
       2025 年 4 月 28 日   ❤️ 2
    你先把这一百多个被毙了的提案看一下吧,可以说 99%的人能想到的方法里面都有。
    https://github.com/golang/go/issues?q=label:error-handling
    zzhirong
        3
    zzhirong  
    OP
       2025 年 4 月 28 日
    @PTLin 其实我也没感觉 if err != nil { 有太大问题, 只不过语法上确实看起来有点冗余了点。
    pike0002
        4
    pike0002  
       2025 年 4 月 28 日
    Subilan
        5
    Subilan  
       2025 年 4 月 28 日 via iPhone
    我觉得最难顶的还是频繁多次错误处理的场景,err 变量不能重定义,要么事先专门声明一个,要么专门为每一个错误单独起个名字来定义。有更好的办法吗?
    vczyh
        6
    vczyh  
       2025 年 4 月 28 日
    就用 err.nn 吧,别的越用越麻烦,实在不行就换语言吧
    vczyh
        7
    vczyh  
       2025 年 4 月 28 日
    Subilan
        8
    Subilan  
       2025 年 4 月 28 日 via iPhone
    @vczyh 确实是一个方法,但是 if 可能会堆特别长,如果这个函数有其他数据返回,剩下的操作也只能在 if 里面做😐😐😐
    zzhirong
        9
    zzhirong  
    OP
       2025 年 4 月 28 日
    @pike0002 哈哈, 算个极端例子吧, 处理上抛这种情况确实不优雅了点。
    vczyh
        10
    vczyh  
       2025 年 4 月 28 日
    @Subilan 我一般这么写: https://imgur.com/a/x4qtu9A
    aloxaf
        11
    aloxaf  
       2025 年 4 月 28 日   ❤️ 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  
       2025 年 4 月 28 日
    讲究工程性的工程师多数会觉得 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
       2025 年 4 月 28 日
    @lesismal 我也认为显式比隐式更好, 目前吐槽最多的应该是, 显式上抛以及层层上抛(导致了很多重复的代码), 在翻看 #2 给出的链接中的提案时, 发现一句话, "The goal is not to replace all error handling. The goal is to replace the most common case", 我也很认可, 针对高频的用法做些优化, 也未尝不可。
    log4j
        14
    log4j  
       2025 年 4 月 28 日
    习惯了表示还行,显示处理 error 工程上来讲还是很好的
    Subilan
        15
    Subilan  
       2025 年 4 月 28 日 via iPhone
    @vczyh #10 我得感谢 Go 没有禁止这种情况下的 err 重定义(但会被 IDE 画上线)
    zzhirong
        16
    zzhirong  
    OP
       2025 年 4 月 28 日
    @log4j 确实, 对代码阅读也很友好, 相对于, 一个小小的 ? 就可能是一个函数出口来说, 好多了。
    Hopetree
        17
    Hopetree  
       2025 年 4 月 28 日
    我现在写 Python 还经常借鉴 Go 的这种显示的异常语法,Go 又不是强制让你这么写,你要是不想判断你就直接异常不就行了
    henix
        18
    henix  
       2025 年 4 月 28 日
    我比较喜欢 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  
       2025 年 4 月 28 日
    go 通过返回值判断错误的方式我觉得挺好的,现在写 CPP 很多我都这样写,用 pair 或者 tuple, cpp23 有了 expected,

    go 的问题就是要写太多的 if err != nil , 如果能使用 check, try, ? 关键字或者符号,简化一下代码,那真的非常爽了
    chor02
        20
    chor02  
       2025 年 4 月 28 日
    我也不明白为什么不能有默认如果 err 不为空直接返回 err 的方案
    ltaoo1o
        21
    ltaoo1o  
       2025 年 4 月 28 日
    第一次接触到这种错误处理方案还是在 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  
       2025 年 4 月 28 日
    这个问题讨论了八九年了吧 ?
    server
        23
    server  
       2025 年 4 月 28 日
    各有各的优势,各有各的问题,习惯就好了
    huigeer
        24
    huigeer  
       2025 年 4 月 28 日
    习惯就好,
    zzhirong
        25
    zzhirong  
    OP
       2025 年 4 月 28 日
    @LawlietZ 严格来说, 从 Go 正式发布开始就没停过吧, 后来伴随 Rust, TypeScript 等引入新的语法, 讨论又一次次开始, 我其实就是想看看这么多年过去了, 有无更优雅的方式出现。
    关于   ·   帮助文档   ·   自助推广系统   ·   博客   ·   API   ·   FAQ   ·   Solana   ·   2060 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 00:35 · PVG 08:35 · LAX 16:35 · JFK 19:35
    ♥ Do have faith in what you're doing.