V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
wkong
V2EX  ›  程序员

个人觉得 Go 的 error 设计的非常好,为什么还那么多人吐槽?

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

    一个程序是否健壮,主要判断是是否对异常有精准处理。

    像 Java 异常的处理虽然少写了代码,但是增加了未知性。

    Go 虽然多了一些代码,但是很容易写出健壮性的程序。孰轻孰重这不是很明显吗?

    156 条回复    2024-05-17 14:04:26 +08:00
    1  2  
    CrazyMonkeyV
        1
    CrazyMonkeyV  
       344 天前   ❤️ 7
    你对 JAVA 的异常处理有误解。
    dongtingyue
        2
    dongtingyue  
       344 天前   ❤️ 6
    javar 比较多他们习惯自己那一套,用其他语言就爱用原来的套到新的上面。
    wkong
        3
    wkong  
    OP
       344 天前
    @CrazyMonkeyV 什么误解?我也做过几年的 Java 开发
    mars2023
        4
    mars2023  
       344 天前
    Java 的异常,应该对比 go 的 Panic 吧。
    wkong
        5
    wkong  
    OP
       344 天前
    @mars2023 不是,Java 的崩溃异常才能对应 Go 的 Panic
    Goooooos
        6
    Goooooos  
       344 天前   ❤️ 1
    你说的都对
    Maboroshii
        7
    Maboroshii  
       344 天前   ❤️ 2
    我比较喜欢 python 的异常,没有个 10 层缩进那叫写代码吗? doge
    Ayanokouji
        8
    Ayanokouji  
       344 天前   ❤️ 21
    1. go 的 error 类型太弱,只能靠字符串判断
    2. 多层 error ,如果直接返回,溯源太难(原生 error 无调用栈),如果追加信息,就会有 1 的问题,是什么错误类型难判断
    3. 如果不介意多代码,java 完全可以全部是 checked exception ,自带类型和调用栈
    nagisaushio
        9
    nagisaushio  
       344 天前   ❤️ 7
    因为本可以更好,rust 和 go 的异常原理是一样的,但 rust 的语法糖就很香。但凡 go 多个语法糖也不会这么多人吐槽
    pursuer
        10
    pursuer  
       344 天前   ❤️ 3
    虽然不知道第几次看到类似讨论了...throw catch 是一种函数多级退出的在控制流上语法糖,go 里对应的是 panic ,Java 你写个 return Multivalue<Result,Error?>也不是不行
    cyp0633
        11
    cyp0633  
       344 天前
    离 rust 就缺那么一点儿
    zihuyishi
        12
    zihuyishi  
       344 天前
    其实这个错误处理很早的时候 windows 的 com 组件就是这么设计的,所有的 com 相关 c++接口都返回一个 HRESULT 需要处理,然后这个 HRESULT 在 c#和 vb 这种语言表现就是抛异常。包括后来 javascript 的 promise 也是差不多,最开始是.catch()处理,在 async 函数里就是 try catch
    golang 比较特别的其实在于你不好忽略一个 error ,不处理编译不通过,直接用'_'忽略 static check 也会抱怨,导致用户不得不处理这个错误,虽然有些烦,但是我认为是很好的设计。反倒是之前太多人错误处理都太草率了
    wkong
        13
    wkong  
    OP
       344 天前
    @Ayanokouji

    1. try catch 判断比字符串判断高级些吗?越简单明了的设计反而是好的设计。

    2. 多层 error ,可以在最底层的 error 打个 log ,就知道来源了

    3. 不仅仅是多写代码问题
    musi
        14
    musi  
       344 天前   ❤️ 4
    一个函数里面加了十几个 if 就是为了判断程序有没有错。。。
    xiangyuecn
        15
    xiangyuecn  
       344 天前   ❤️ 1
    On Error Resume Next
    On Error GoTo 0

    @Maboroshii 那就不得不提上古时期的 vb 语法了,搁现在也是很优雅的,比 python 的强制缩进好看 100 倍 至少人家还有 end 明确收尾😅
    rrfeng
        16
    rrfeng  
       344 天前 via Android
    感觉就缺个枚举类型
    如果有,可以把业务 error 都用枚举写出来,就不存在字符串判断的问题。
    MoYi123
        17
    MoYi123  
       344 天前
    其实是认为 resp, err 这样的写法有 4 种情况
    1. nil, err
    2. nil, nil
    3. resp, err
    4. resp, nil

    而实际上只有 1 和 4 是比较标准的格式, 没有在语言层面上禁止 2,3 这 2 种写法, 虽然我觉得根本无所谓.
    Ayanokouji
        18
    Ayanokouji  
       344 天前
    @wkong 你去看看多少代码 if err!=nil 直接 return 的,一个业务那么多 error ,怎么确定哪个是底层已经打印日志了。
    调用其他同事的方法,难道还要去看看他写的源码,error 返回的是啥,看看 fmt.errorf("")里边写的啥?
    cmdOptionKana
        19
    cmdOptionKana  
       344 天前
    @musi 如果认为没必要判断处理,完全可以让它 panic ,并非全部错误都要特殊处理。语言是死的,人是活的。
    ho121
        20
    ho121  
       344 天前 via Android
    有人写过 C 语言中的错误处理吗?
    emSaVya
        21
    emSaVya  
       344 天前
    @wkong EAFP LBYL 两种风格罢了。有什么高下之分吗?

    而且 try exception 明显是更优雅的方案, 只不过会带来一些开销。
    littlewing
        22
    littlewing  
       344 天前
    写 C/C++ 的都没说什么呢?
    大部分 C++ 工程代码是不会用 exception 那一套的
    mightybruce
        23
    mightybruce  
       344 天前
    因为大多数这种人都是写 java 的,喜欢这种 try catch 异常处理, 对于非业务开发,懒散和滥用 try catch 处理可以导致致命的 bug ,
    linux 内核 和 c 那么多库都没有 try catch, 方式比 go 还原始也没啥人说什么,都是业务开发太闲了。
    go 的哲学就是 错误就是要显示检查处理的,异常处理的不可见错误检查所带来的问题其实是很大的。不少业务开发才不管那么多,还不是能跑就行,最多加个单元测试。
    nomagick
        24
    nomagick  
       344 天前   ❤️ 4
    你对“设计”这个词是不是有什么误解...

    Golang 的异常处理只能说是没有设计,而不能说是设计得好或设计得不好
    Goooooos
        25
    Goooooos  
       344 天前   ❤️ 2
    @mightybruce try catch 不是 java 特有,python ,c#,javascript 都有
    hedwi
        26
    hedwi  
       344 天前
    你说得对 很多人其实并没有处理所有的可能出现的错误
    xiuxian
        27
    xiuxian  
       344 天前
    @littlewing 我写 C++也基本不用,exception 使用起来是为了取代返回多个错误码,一个个的去判断。 实际上不能像 java 和 go 一样,全局捕获异常。该 carsh 还是得 carsh.
    xiuxian
        28
    xiuxian  
       344 天前
    crash
    mightybruce
        29
    mightybruce  
       344 天前
    另外 try catch exception 还涉及到 RAII 机制,go 连对象都没有,谈 try catch 在 go 里面是没什么意义的,最多也只是语法糖。
    CloveAndCurrant
        30
    CloveAndCurrant  
       344 天前
    其实 go 的 error 相比 rust 的最大缺陷就是不能自动传导,rust 使用?就可以将 error 传导到函数最终结果,go 需要手动才能做到。
    joycelin
        31
    joycelin  
       344 天前   ❤️ 4
    是这样的,以前也说不需要泛型,我看以后新的错误处理方式出来了你再怎么说
    gitrebase
        32
    gitrebase  
       344 天前
    @Ayanokouji #18 写 Go 一般在最外层打日志吧,比如在 handler ;“if err!=nil 直接 return”在我的工作经历中,几乎不会有这样的代码,多少都会带点上下文
    CloveAndCurrant
        33
    CloveAndCurrant  
       344 天前
    @CloveAndCurrant 除了 rust ,java ,Python 这些都可以做到将异常自动传导的
    chendy
        34
    chendy  
       344 天前
    看具体工作内容吧
    要是写什么底层逻辑这套东西可能是好用的(因为我没写过)
    但是写业务弄这么复杂的错误处理逻辑,系统和开发必死一个
    mcfog
        35
    mcfog  
       344 天前
    你看,即使在这楼里偏支持的声音,也有挺多并不太理解比较现代的那一版 errors.Is/As 新设计,还有如何结合 golang 偏鸭子类型的 interface 设计做复杂 error 交互的同学的。所以我是觉得 Golang 的 error 设计唯一的问题就是可能低估了大家学习一个和传统非常不一样(但是确实精妙)的设计这件事情的成本。

    也包括相关的 interface 设计,至少我感觉多数人都要教,而且教了以后也不像其他概念基本一次性掌握,得反复 review 。
    june4
        36
    june4  
       344 天前
    一般程序中很少处理异常,都是默认往上抛,直到顶层处理,比如直接返回 5xx 错误,所以 java 这种就不干扰正常代码。

    少量函数天生大概率需要就近处理异常,这些函数你再用错误码也不迟,比如我最近看的 js 验证处理库每个函数都有二个版本,一个是 xxx()抛异常,一个是 safeXXX()无异常会 return 错误码,这不二全了吗
    mcfog
        37
    mcfog  
       344 天前   ❤️ 1
    @june4 现在有了 generic 可以搞一个非常棒的 helper

    func Must[T any](v T, err error) T {
    if err != nil {
    panic(err)
    }
    return v
    }

    就是泛化的 https://pkg.go.dev/html/template#Must
    boboaiya3
        38
    boboaiya3  
       344 天前
    @wkong #13
    boboaiya3
        39
    boboaiya3  
       344 天前
    @wkong #13
    try catch 判断比字符串判断高级些吗?越简单明了的设计反而是好的设计
    zhanshen1614
        40
    zhanshen1614  
       344 天前
    大多数人习惯 try catch 捕获异常肯定会觉得 go 的异常处理不方便。go 的异常处理比较麻烦但很详细可以知道是哪个步骤错误,阅读更清晰。try catch 捕获异常要考虑 exception 的从属关系,而且抛出异常也不一定知道是哪里执行错了还得看报错的文件、所在行,需要借助错误日志定位问题。
    boboaiya3
        41
    boboaiya3  
       344 天前
    @wkong #13
    try catch 判断比字符串判断高级些吗?越简单明了的设计反而是好的设计?
    这个是错误,Java try catch 实现 是基于 c 里面的( non local jump ),这种一种特殊的函数跳转方式,肯定和普通函数执行不一样,性能层面,各方面都存在优势
    ShadowPower
        42
    ShadowPower  
       344 天前
    可以看看 Rust 的 Result 设计,你会对错误处理有一个全新的认知……
    thinkershare
        43
    thinkershare  
       344 天前   ❤️ 1
    go 这种异常设计,就是没有设计,最原始的 C 就是这种模样。go 的 整个设计是简陋而不是简洁,随着需求的增大,go 一定会变成和 C++一样难看。
    43n5Z6GyW39943pj
        44
    43n5Z6GyW39943pj  
       344 天前
    Java 看日志的时候很狼狈,不说了 等下要被喷
    songray
        45
    songray  
       344 天前   ❤️ 4
    你搞错了几件事.
    Java 、JS 、Python 完全可以做和 Go 一样的 err. 以 js 为例, 虽然没有多返回值, 但你每个方法返回 { err: boolean, value: any } 就行.
    err 的问题在于代码噪音太大, 进行多次相同操作, 唯一也是最好的办法反而是模拟一个蹩脚的 err monad, 参考 Rob 的文章 errors are values.
    这个问题 Go 是可以语言层面进行简化的, 但 go 一直没有这么做.
    更直白来说, 如果开发者一直在模拟 monad 或者 try catch , 那 go 官方为什么不做进 language level 呢?
    happyxhw101
        46
    happyxhw101  
       344 天前
    楼主你 out 了, 不管你怎么想, 你要跟上时代潮流, 现在的潮流是 rust, 所以 ....
    bwangel
        47
    bwangel  
       344 天前   ❤️ 3
    error 没有栈信息,配合 pkg.errors 的 WithStack 使用,增加了开发者的心智负担。

    例如以下调用链

    func api()

    func service()

    func rpc()

    api -> service -> rpc

    rpc 返回了一个错误,一层一层最终返回给了 api, api 拿到错误后,需要记录日志,它想知道错误是哪里来的,此时就需要栈信息。

    目前的解决办法,是 rpc 返回错误时,error 用 pkg.errors ( https://github.com/pkg/errors/blob/master/errors.go) 的 Wrap 包装一下,service 包装时,就不能用 wrap 了,需要用 WithMessage ,要不然会出现两份栈信息。

    这要求开发者对代码的层次结构非常清楚,哪些函数是最底层,哪些是上层。

    你想想,你刚接手了一个 5w 行的项目,读了三天代码之后开始写一些小 feature 。这时候你能了解清楚哪些函数是最底层吗?这样很容易就写错了,然后错误信息里面可能就有 N 份栈信息。
    mainjzb
        48
    mainjzb  
       344 天前
    因为他们没写过 C/C++,没写过 windows api
    gam2046
        49
    gam2046  
       344 天前
    go error 的设计,好是好,但是 error 本质上是个字符串,这玩意不太好。如果可以包含一个类型信息就会好很多。
    yazinnnn0
        50
    yazinnnn0  
       344 天前
    因为现代语言大多可以用 monad 去处理这种问题 (比如 error, nil/null, future/promise)

    时髦一点的语言甚至内建了 monad comprehension 去处理 monad 地狱的问题

    但是 go 给的方案是 c 派的返回 errno 的方式, 也许 go 的哲学类似于 c, 但是 go boy 跳出来吹 err != nil 是令人困惑的
    Ayanokouji
        51
    Ayanokouji  
       344 天前
    @gitrebase 最外层打印日志也有点问题,如果调用链太深或者封装的 error 太多,还是不方便直接看出错误源,error 不自带调用栈,我觉得这是最难接受的
    yazinnnn0
        52
    yazinnnn0  
       344 天前
    rust 和 go 一样个毛....
    rust kotlin fsharp swift 都从 ocaml 里抄了不少设计, dart 未来也会向 ocaml 方向靠

    go 有一点 ocaml 的影子吗
    otakustay
        53
    otakustay  
       344 天前
    go 这套显然和 shell 是一样的,有人喜欢 shell 的异常处理吗
    qianzanqi
        54
    qianzanqi  
       344 天前 via Android
    @bwangel 如果 service 有两处调用 rpc 就还得依靠 message 内容来区分调用处了。

    我一直觉得 go 处理异常要么无脑 WithStack 这个就相当于 java 的一句一 try ;要么无脑 WithMessage 传个随机字符串,查调用栈只要在项目里搜索就行,还没有运行时的性能问题
    flyqie
        55
    flyqie  
       344 天前 via Android
    go 写业务是折磨,错误处理这边啥玩法都有。。
    MegrezZhu
        56
    MegrezZhu  
       344 天前
    要求函数迪调用方每次调用都显式地验证错误是否存在,又不提供语法糖才是问题的根源。
    事实上我还挺喜欢这种 error pattern 的,在我司的 C++代码里面大量存在着 StatusOr<RetType>的用法,但它和 Go 不一样的是 C++有宏啊,绝大多数场景下我都不需要写`if (result.ok()) {}`,直接用一些`RETURN_IF_ERROR(func())`, `ASSIGN_OR_RETURN(auto ret, func())`就行了,没有什么重复性的代码要写……
    me1onsoda
        57
    me1onsoda  
       344 天前
    增加了未知性,这是编程规范层面能解决的。禁止 catch exception runtimeexception 这种模糊的异常。但 go 就很影响编程体验。。
    dacapoday
        58
    dacapoday  
       344 天前   ❤️ 1
    @Ayanokouji 学艺不精,go 的 error 是接口,只是多数人 偷懒 或 习惯使用标准库的 字符串 实现。
    想要调用栈完全可以自己加,相关的 error 库也非常多。
    关键是 go 给了你选择,运行时收集调用栈是有开销的。
    开发一些偏底层的库时,调用栈信息又完全不够,error 接口 意味着可以自定义 数据结构 只收集关键信息。
    对于调用者,对 error 接口进行类型推断,又能很方便的滤出 是底层系统 error, 还是自定义 error struct ,还是中间各层业务封装的 字符串 error
    nothingistrue
        59
    nothingistrue  
       344 天前
    学艺不精。参见
    @Ayanokouji #7
    @pursuer #9
    @songray #44
    buffzty
        60
    buffzty  
       344 天前
    go 的错误处理跟 c 语言一模一样,没有一个人喷 c 但是喷 go 的挺多的
    再者说了 c 和 go 都有 try catch 模式,c 里面用 goto ,go 一般用 errgroup 也有可以用 goto
    Huelse
        61
    Huelse  
       344 天前
    其实判断这个设计好不好很简单,看其他语言有没有类似的讨论即可。
    像 rust 就不会有这类讨论,当然 rust 有其他的问题,但至少在错误处理上是没什么可争议的,反观 go 争议就特别多。
    lujiaxing
        62
    lujiaxing  
       344 天前   ❤️ 1
    我就这么说:
    在 Java 或者 ASP.NET Core 程序里, 你是可以做全局异常捕获的. 甚至如果底层框架做得好的话, 还可以把 throw Exception 作为中断请求的手段. 当请求数据不正常的情况下, 不需要你一层一层的从逻辑层 return 到展示层, 从子系统 A return 到子系统 B 然后再 return 到 api 层. 直接:


    ```
    def getById(id):
    if id is None:
    raise BusinessException("ID 都不给我你问你马呢???");

    ......
    ......
    ......
    ```


    就可以了, 这句话不需要程序员任何的处理就可以很优雅的返回给 GUI 层.
    如果是 go, 那就要一层一层的定义接收异常信息的参数. 然后像洋葱一样一层一层一层一层的去返回.

    你跟我说这种设计非常好??????????
    mightybruce
        63
    mightybruce  
       344 天前   ❤️ 1
    @buffzty try catch 不是 goto 的封装,在 C 中实现 try catch 也不是一种方式,简单一点是 setjmp longjmp,
    很多代码是从汇编的方式实现的。
    try catch 这些和对象资源也是绑定的, 在 C 中实现的也是个模拟的语法糖,对象资源回收是不好实现的

    Rust ,它里面也是满屏幕的.unwrap(),这实际上跟 Go 的 if err != nil 一样
    写业务的还是太闲了, 天天讨论这些。
    TiaoYeTaiLang
        64
    TiaoYeTaiLang  
       344 天前
    因为 golang 一开始就不是为了开发业务系统用的,而是用于开发中间件系统这种偏底层应用的,对于底层应用来说 error 无非就那么几种,要不计算异常、要不 IO 异常。
    cosiner
        65
    cosiner  
       344 天前
    错误是 error, 对应 java 的 checked exception, 异常是 panic, 对应 unchecked exception

    go 的问题是 if err != nil 太频繁,因为大多数 go 程序都是 io 类型的,io 的 error 情况非常多,所以才会这样
    换成 compute 类型的,就没那么多 error 了
    go 就相当于 java 里面每一个 checked exception 都要手动 throw ,缺少一个语法糖

    至于错误处理都是一样的,go 里面不要用 _ 忽略错误,java 不要 try catch {}不处理
    jhdxr
        66
    jhdxr  
       344 天前
    @wkong #13
    别人都说出来了你却还能理解偏,只能说明你太菜。
    重点不是 **try catch** 比 **字符串比较** 高级,而是 **异常类型** 比 **字符串** 高级。
    chenqh
        67
    chenqh  
       344 天前   ❤️ 4
    @MorJS 不是啊,你不打印堆栈的话,java 的错误看起来不就是和 golang 一样了吗?但是你愿意看这种错误吗?没有堆栈的 err
    根本排查不了,golang 不就是这样,就凭一个 err 鬼知道到底是因为什么出错的,没有堆栈拿头排查
    serialt
        68
    serialt  
       344 天前
    写一下 shell 你就会习惯 go 的错误处理
    bv
        69
    bv  
       344 天前
    掌握流量密码,撕比确实能招来一大堆回复。
    pkoukk
        70
    pkoukk  
       344 天前
    @Ayanokouji #8 try catch 和 error 不是一回事,try catch 对标的是 panic 。
    neoblackcap
        71
    neoblackcap  
       344 天前
    @chenqh 也许他们都喜欢用 core dump 分析问题
    chenqh
        72
    chenqh  
       344 天前   ❤️ 1
    @neoblackcap 可能用 golang 的都是大佬呗,不用堆栈,仅仅 print err 就可以知道到底是什么原因导致的错误,反正我不行
    lesismal
        73
    lesismal  
       344 天前   ❤️ 2
    看名字就知道了,一个是错误处理,一个是异常处理。

    go 用 panic 照样能像 java 那样处理异常,但本来就是为了处理错误所以用 if error 怎么了?而且哪里有错就现场处理、代码的可读性更好。只是那些其他语言习惯了用异常的人一直不这样搞才会觉得别扭。你看写 c 的人有几个说 go 错误处理垃圾的?即使 cpp ,也少有项目把错误处理都搞成 java 那种用异常处理的方式

    反倒是 java 和一些其他语言,错误处理的场景和异常处理的场景,都用处理异常的方式来处理。习惯了自己的这种用异常处理去处理错误的方式,然后来鄙视 go 的该用啥用啥的鲁棒性,这帮人可真逗
    lesismal
        74
    lesismal  
       344 天前   ❤️ 2
    go 这块的本质就是:
    用错误处理的方式处理错误
    用异常处理的方式处理异常

    java 那些:
    用异常处理的方式处理错误
    用异常处理的方式处理异常

    好奇你们哪里来的自信喷 go
    lxdlam
        75
    lxdlam  
       344 天前   ❤️ 3
    单从两种范式上来说,值类型跟异常类型错误直接比较很难比较清楚:

    - 值类型还是将错误当作值,本身没有控制流操作,所以当作值处理可以轻松当作比如 data 的一部分到处传递处理等等,缺点就是缺乏强制力,简单把值 ignore 掉就可以丢掉错误,而大家都倾向于懒一点;
    - 异常类型可以打断并接管控制流,更加强大,guarantee 也更好,如果 runtime 实现得不错(比如 Java )那用起来其实还是很舒服的,缺点就是这个特性很容易被误用(相信大家都听说过或者见过用 exception 传值的业务代码)。

    两种范式各有优劣,其实没有直接关系,语言风格而已。

    而单纯讨论值类型错误,Go 的实现有自己的优势。错误这个东西本身就是很容易产生很多 concrete type 的,基于 duck typing 的 interface 化,其实大大简化很多操作,比如直接把已有 error wrap 起来,然后实现一下 `Error` 和一些 helper function ,就能快速搞一个新的错误出来,开发效率是很高的(没有 thiserror 和 anyhow 之前,Rust 搞个自定义错误有多痛苦,相信很多朋友深有体会)。
    Go 最大的问题其实在于,他采用了值类型错误,但是类型系统支持非常 basic ,导致大家处理错误非常痛苦。比如在类型系统比较现代的语言中,这种双类型语言或多或少能实现一定程度的 monad ,这样我们就可以写出类似于:
    ```
    let val = some_function().map(fun).map(foo).map(bar);

    match val {
    Ok(v) => println!("Value is: {}", v),
    Error(e) => println!("Failed! error: {}", e),
    }
    ```
    在这里面,我们我们确实不关心中间错误,直接 chain 起来,处理最后错误就行,而如果我们关心比如到某一步的错误,我们单独接一下这一步的结果就可以拿到中间结果再做处理。无论是看起来还是读起来,其实都非常清晰明了。而可惜的是,Go 因为一开始设计压根没想这个问题,导致现在的打补丁的 generics ,很长线看起来都很难支持这种比较完备的 monad 特性。所以,我们只能一步步 if-else ,或者将这三个函数塞到一个 slice 里面,用 `reflect` 循环每个函数 apply 去每一步拿一下 type 信息做一些非常丑的补丁。
    稍微补一句:为啥这里需要比较好的类型系统才能实现?在上面的流程中,实际上 `map` 接到的函数签名类型是各不一样的,有比较好类型系统的编译器在这里可以做非常好的静态推断;或者我们完全交给动态类型,损失一定 runtime 性能也能做到。而可惜的是,Go 既要选择尽量静态类型,又没把静态类型做很好,就卡在这了。
    lxdlam
        76
    lxdlam  
       344 天前
    @lxdlam typo:这种双类型语言 => 这种支持双类型(甚至更多类型)组成异构容器的语言
    bthulu
        77
    bthulu  
       344 天前   ❤️ 1
    @lesismal java 也可以用处理错误的方式处理错误的. java 没限制你的返回类型, 你可以返回一个 result<err, data>, 然后再处理就是.
    bronya0
        78
    bronya0  
       344 天前
    如果一个编程语言的设计真的很好的话,为什么会有这么多人吐槽?为什么 java 、python 基本不吐槽这个?
    bronya0
        79
    bronya0  
       344 天前
    go 刚出的时候,这些都是人人吐槽的垃圾设计,过了很多年出了 k8s 和 docker 后,就开始有人舔了
    buffzty
        80
    buffzty  
       344 天前
    @mightybruce linux 源码都是用 goto 做的 try catch 。setjump 也可以做 goto 比较简单
    #include <stdio.h>

    typedef struct Ex
    {
    int code;
    char* msg;
    } _Ex;

    void exception()
    {
    _Ex* ex; // try {
    // xxx do something
    if (1) // throw
    {
    _Ex tmp_ex = {1, "xxx error"};
    ex = &tmp_ex;
    goto _handle_ex;
    }
    // xxxx do something
    goto _no_ex; // } end try
    _handle_ex: // catch {
    ex != NULL && printf("Exception: %d, %s\n", ex->code, ex->msg);
    _no_ex: // } end catch
    }

    int main(int argc, char* argv[])
    {
    exception();
    return 0;
    }
    somebody1
        81
    somebody1  
       344 天前   ❤️ 4
    看完 13 楼的回答,我从眼睛到心灵都被震惊了!!!

    “1. try catch 判断比字符串判断高级些吗?越简单明了的设计反而是好的设计。”

    对,就是高级啊!!!!

    类型判断就是比字符串判断高级啊,这还用说嘛!!!全是字符串判断要累死人啊,但是类型判断很简答啊,而且更符合对业务的代码表达!!!!

    你写个一两百行的代码,当然字符串判断很简单。要是几万行呢,一个一个字符串判断???你的字符串手册都得搞个几百页吧!!!

    购物业务的库存不足异常,请用 A 字符串,库存锁定请用 B 字符串。
    订单的查询失败请用 C 字符串,无此订单请用 D 字符串。
    安全策略失败请用 D 字符串,安全策略未知异常请用 E 字符串。
    然后一直 2000 个字符串。

    你真的写过代码吗?一定要回答我这句话!!!
    yannxia
        82
    yannxia  
       344 天前
    @lesismal 错误和异常也是主动区分出来的(无对错之分),对于 java 来说,只有 expection ,最多分 checked 和 unchecked 。Error 就是一个普通对象( GO 也是),这不过 Go 支持多返回值,大家就基于这个特性搞了这个用法,也可以自己封装个 Result 出来用。

    说到底看场景,
    - 大多数 Java 开发者面对的是业务系统,系统中一旦出现了 error ,大多数情况下是需要中断整个链路的,而且因为有 GC ,不用考虑 RAII 的问题,直接原地回到顶结束就好了。所以这个成了惯用法。
    - Go 的开发者在早期纯业务的很少,主要写一些 CNCF 那些中间件,考虑点和业务系统不一样,希望精细的掌控错误,现在这套就还也还行。

    立场主要看写啥,我拿 go 写业务系统的时候,我也挺讨厌 error 的,99%的时候除了往上抛,又能做什么呢。但是 java 写中间件的时候,用 checkedexpection 体验也不是挺好。
    xFrye
        83
    xFrye  
       344 天前
    go 有挺多不错的地方,但为什么就偏偏去吹 error 的设计呢
    xausky
        84
    xausky  
       344 天前
    @lujiaxing go 语言是让你有得选而 Java 很多时候没得选

    比如 Go 想要实现你发的框架统一处理

    ```
    if err != nil {
    panic(err)
    }
    ```
    结合有人说 Must Helper 更简单
    而有的东西比如就是可以处理的,Java 有时候就非要让你 try 比如
    ```
    Map<String, Object> result = new HashMap<>();
    try {
    result = new ObjectMapper().readValue("{}", new TypeReference<HashMap<String, Object>>(){});
    } catch (JsonProcessingException e) {
    // ignore
    }
    ```
    而 go
    ```
    var result map[string]interface{}
    _ = json.Unmarshal([]byte("{}"), &result)
    ```
    bronya0
        85
    bronya0  
       344 天前
    go 写业务就是一坨答辩,不用怀疑,可以认为没有错误机制,很原始,当成增强的 c 用就行了
    thinkershare
        86
    thinkershare  
       344 天前
    @yannxia 你这个算是客观的评价,所以我感觉 go 真的只适合做中间件,用来写业务,怎么都是难受,用 go 写过 2 个 web 项目后,感觉 sprint/asp.net core/Koa 写起来舒服得多。如果未来要有好的业务应用程序开发体验,只能和它最初的哲学原则越来越背离。
    yazinnnn0
        87
    yazinnnn0  
       344 天前   ❤️ 2

    https://fsharpforfunandprofit.com/rop/

    Scott Wlaschin 做过 error handling 方面的 monad 科普

    https://fsharpforfunandprofit.com/rop/rop427.jpg
    lujiaxing
        88
    lujiaxing  
       344 天前
    @xausky 问题人家 java 里出现异常是要求中断的. 无论如何后面都不能继续执行, 直接回到调用栈的最顶层. 我知道 go 可以全局 if err != nil. 问题是如果要求抛出异常之后中断后续全部逻辑, 阁下又当如何应对? 抛出的不是程序逻辑错误而是业务逻辑设计上的异常情况, 你不还是要一层一层的 if err != null 么?
    wyx119911
        89
    wyx119911  
       344 天前
    @thinkershare #43 C/C++起码还有宏啊,并不难用也不难看
    lujiaxing
        90
    lujiaxing  
       344 天前
    @xausky @yannxia 说的很明白了. go 写业务就是一坨屎. 业务越是复杂, 逻辑分支越是多, go 写起来越是恶心. 但是用来写一些很底层的中间件倒是很方便的.
    ShadowPower
        91
    ShadowPower  
       344 天前
    @lxdlam #57
    我想到了很久以前看过的一篇“面向铁路编程”,里面的内容好像差不多……
    稍微找了一下
    https://fsharpforfunandprofit.com/rop/
    ZeroDu
        92
    ZeroDu  
       344 天前
    @MorJS #44 看 golang 日志那才是费劲,js ,java ,python 这种一看就可到哪里有错误了
    lxdlam
        93
    lxdlam  
       344 天前
    @ShadowPower 上面 #67 老哥也提到了,其实这个范式在函数式社区很常见,我们重点不关注你是不是所谓的“错误”,而关注类型本身,考虑用类型做 matcher 去配合值做 transformation 。这些年一些工业界的看起来很新的方法都来源于此,比如经典的 parser combinator ,如果你把 `Result<T>` 这个二元类型异构容器思考成 `Pair<&str /* remain input */, Result<T /* Token type */>>`,其实这里面的函数组合跟我上面说的 error monad 是一致的。更进一步来说,函数式的底层数学抽象 lambda calculus ,其实就是一种 combinatory logic ,天生以组合为主。在这种背景下,函数式语言采用这种思考是非常直接的。
    bronya0
        94
    bronya0  
       344 天前
    Go 语言的 `error` 类型设计是一种简单而有效的方式来处理错误。它具有以下优点:

    * **简单易用:** `error` 类型是一个内置类型,不需要任何额外的库或框架。它可以很容易地与其他语言的错误处理机制集成。
    * **类型安全:** `error` 类型是一个接口类型,这意味着它可以表示任何实现了 `Error()` 方法的类型。这使得错误处理更加类型安全,因为编译器可以检查错误类型是否与预期的一致。
    * **可扩展性:** `error` 类型可以很容易地扩展,以支持自定义错误类型。例如,我们可以定义一个 `MyError` 类型,它包含额外的错误信息或上下文。

    然而,Go 语言的 `error` 类型设计也有一些缺点:

    * **缺乏详细的错误信息:** `error` 类型本身只包含一个错误消息。这使得调试错误变得困难,因为我们无法获得有关错误的更多信息。
    * **没有错误代码:** `error` 类型没有提供错误代码。这使得在不同系统之间共享错误信息变得困难,因为每个系统可能使用不同的错误代码。

    为了解决这些问题,我们可以使用一些第三方库或框架来增强 Go 语言的错误处理机制。例如,我们可以使用 `github.com/pkg/errors` 库来包装错误并添加额外的上下文信息。我们也可以使用 `github.com/go-kit/kit/log` 库来记录错误并添加额外的元数据。

    总体而言,Go 语言的 `error` 类型设计是一种简单而有效的方式来处理错误。它具有简单易用、类型安全和可扩展性的优点。然而,它也有一些缺点,例如缺乏详细的错误信息和错误代码。我们可以使用一些第三方库或框架来增强 Go 语言的错误处理机制,以解决这些问题。

    ## 改进建议

    以下是一些改进 Go 语言错误处理机制的建议:

    * **在 `error` 类型中添加错误代码:** 这将使在不同系统之间共享错误信息变得更加容易。
    * **提供一种标准的方式来记录错误:** 这将有助于确保错误信息以一致的方式记录下来,并包括所有相关元数据。
    * **鼓励使用自定义错误类型:** 这将使错误信息更加具体和有意义。
    * **提供一种标准的方式来包装错误:** 这将使我们可以轻松地将错误从一个系统传递到另一个系统,而不会丢失任何信息。

    通过这些改进,我们可以使 Go 语言的错误处理机制更加强大和灵活。
    lujiaxing
        95
    lujiaxing  
       344 天前   ❤️ 1
    @bronya0 请不要贴 GPT 的回答.
    bronya0
        96
    bronya0  
       344 天前   ❤️ 1
    Go 和 Java 两种语言的错误处理机制都有各自的优势和适用场景,而哪个更优秀主要取决于具体的需求和开发者的偏好。以下是对两者的简要比较:

    ### Go 的错误处理机制:

    1. **优势:**
    - **简洁和清晰:** Go 使用多返回值和接口类型的错误,代码看起来简洁而清晰,错误处理直接与函数调用关联。
    - **显式处理:** Go 鼓励显式地处理错误,开发者需要明确检查并处理每一个错误。
    - **错误值为接口:** 错误是一个接口类型,允许开发者自定义错误类型,提供更多的上下文信息。

    2. **劣势:**
    - **容易忽略错误:** 由于错误是常规的返回值,存在一定的可能性开发者会忽略错误。
    - **容易忽略错误:** 由于错误是常规的返回值,存在一定的可能性开发者会忽略错误。

    ### Java 的异常处理机制:

    1. **优势:**
    - **异常层次结构:** Java 引入了异常层次结构,允许开发者根据具体的异常类型进行处理,使得代码结构更加灵活。
    - **支持异常链:** 异常可以包含其他异常,形成异常链,提供更多上下文信息。
    - **Checked 和 Unchecked 异常:** Java 区分 Checked 和 Unchecked 异常,使得开发者能够有选择地捕获和处理异常。

    2. **劣势:**
    - **语法冗余:** Java 的异常处理语法相对冗长,有时需要使用大量的 try-catch 块。

    ### 总体评价:

    - **Go 适用于简单和清晰的代码结构,强调显式错误处理。适用于构建轻量级服务和工具。**

    - **Java 适用于大型企业级应用,具备更复杂的异常处理机制,支持更精细的错误处理。**

    在选择错误处理机制时,可以根据具体的项目需求和团队经验做出权衡。部分团队可能更喜欢 Go 的简洁性和明确性,而部分团队可能更倾向于 Java 提供的异常层次结构和更灵活的处理方式。
    allanpk716
        97
    allanpk716  
       344 天前 via iPhone   ❤️ 1
    看了几个吐槽 go err 问题的,这玩意是简陋,但是你们好歹多用下,或者去看看最佳实践嘛…

    什么 err 是判断字符串的都出来了,那是你不用 errors.is 的问题吧

    喜欢用 go ,就是喜欢死板的处理所有的 err ,培训新人友好,你就把能处理的都处理就好了,从头培养编码要考虑鲁棒性的观念。

    非说 try catch 包裹代码优雅,没错,编码能力好的,确实优雅,看的舒服。问题是在我看到的项目中,绝大多数用法就是嫌弃的一比啊…越是水平一般的,越喜欢滥用 try catch (只代表我看到的项目和人),反正该判断的返回值都不管了,try catch 万能…
    Masoud2023
        98
    Masoud2023  
       344 天前
    因为你 Java 模板代码写多了,所以你才觉得这种糟透了的模板代码没问题。

    你说“但是增加了未知性。”我只能默认你是不了解 Java 的工作细节。
    harry890829
        99
    harry890829  
       344 天前
    @allanpk716 我也是看了看,感觉真正用的深的没几个,errors.is 这个我必须吐槽一下,这里有个 is ,但是要写入的时候一定要 fmt.Sprintf("%w",err),这个文档和 errors 包的文档不在一起,导致很多人是不知道,或者找不到的
    adoal
        100
    adoal  
       344 天前
    golang 里的错误处理,从意图上来看,似乎跟某些语言里用 left/right 、result/error 的带值枚举类型来处理错误有相同的动机。但是人家用的是“和”类型,而它居然用的是“积”类型。
    1  2  
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2690 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 34ms · UTC 12:24 · PVG 20:24 · LAX 04:24 · JFK 07:24
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.