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

写了个 err warp,或许可以少写点 if err

  •  
  •   voidmnwzp ·
    NullpointerW · 2023-04-28 15:43:39 +08:00 · 4272 次点击
    这是一个创建于 609 天前的主题,其中的信息可能已经有所发展或是发生改变。

    一般情况下需要串行化的执行下去,如果其中一步遇到错误就会退出,但这样调用完个每个方法都要写个 err

    png

    如果将执行的方法作为一个闭包封装进去,就可以省去一大堆 if err:

    png

    这样如果执行的过程中遇到了错误,也会跳过执行, 最后调用 wrap.Error()返回错误 e.g.:

    png

    外层变量 a,b,c,d 可以通过闭包来赋值

    47 条回复    2023-05-26 02:06:48 +08:00
    rrfeng
        1
    rrfeng  
       2023-04-28 15:46:28 +08:00
    没变少啊……
    chanyan
        2
    chanyan  
       2023-04-28 15:47:23 +08:00
    sduoduo233
        3
    sduoduo233  
       2023-04-28 15:49:07 +08:00 via Android
    这样怎么确定 err 是在哪一步抛出的?
    learningman
        4
    learningman  
       2023-04-28 17:17:14 +08:00 via Android
    你这写的比 if err 还多
    ljsh093
        5
    ljsh093  
       2023-04-28 17:20:35 +08:00
    还是 4 行一个啊
    777777
        6
    777777  
       2023-04-28 17:24:07 +08:00   ❤️ 10
    就我一个人喜欢写 if err ?简单直接
    blackvv666
        7
    blackvv666  
       2023-04-28 17:26:57 +08:00
    自己用用可以
    hahasong
        8
    hahasong  
       2023-04-28 17:31:35 +08:00
    这还不如用 defer 呢 有种用 java 面向对象打印 hello world 即视感
    hzzhzzdogee
        9
    hzzhzzdogee  
       2023-04-28 17:41:11 +08:00
    其实应该是 wrap?
    zhangxh1023
        10
    zhangxh1023  
       2023-04-28 17:47:30 +08:00   ❤️ 2
    没了 if err 的 golang 都没那味儿了
    helloit
        11
    helloit  
       2023-04-28 17:49:49 +08:00
    errors := map[string]string{
    "a": "a error",
    "b": "b error",
    "c": "c error",
    "d": "d error"
    }
    magicdawn
        12
    magicdawn  
       2023-04-28 17:50:21 +08:00 via Android
    rust 也是 err value ,但是有 unwrap 和 ?存在方便许多
    fgwmlhdkkkw
        13
    fgwmlhdkkkw  
       2023-04-28 17:51:26 +08:00
    ```

    func Must[T any](T val, e error) T {
    if e != nil {
    painc(e)
    }
    return val
    }


    func Must2[T any, K any](T val1, K val2, e error) (T, K) {
    if e != nil {
    painc(e)
    }
    return val1, val2
    }


    ```
    fgwmlhdkkkw
        14
    fgwmlhdkkkw  
       2023-04-28 17:52:52 +08:00
    @fgwmlhdkkkw 😓 哈哈哈哈,,搞混了
    cassyfar
        15
    cassyfar  
       2023-04-28 17:54:33 +08:00
    人才
    cholerae
        16
    cholerae  
       2023-04-28 17:55:20 +08:00   ❤️ 2
    你重新发明了那篇经典的 errors are values 博文中建议的实践。

    https://go.dev/blog/errors-are-values
    Leviathann
        17
    Leviathann  
       2023-04-28 18:10:49 +08:00
    @magicdawn rust 是 value or error 不是 value and error
    TWorldIsNButThis
        18
    TWorldIsNButThis  
       2023-04-28 18:16:51 +08:00
    @cholerae
    这篇经典博文也不过是在重新发明 result/either monad
    也许这就是一种传承
    dobelee
        19
    dobelee  
       2023-04-28 18:24:03 +08:00
    一行没变少,反而增加几行初始化变量。。
    voidmnwzp
        20
    voidmnwzp  
    OP
       2023-04-28 18:25:41 +08:00 via iPhone
    @cholerae 不瞒你说 我就是看了 rob pike 写的 wrap 后才写的
    totoro52
        21
    totoro52  
       2023-04-28 18:27:23 +08:00
    与其叫少写,不如叫遮羞布。。。
    lesismal
        22
    lesismal  
       2023-04-28 18:27:44 +08:00   ❤️ 1
    @777777 #6 你不是一个人!喜欢 if err 的人多着呢,只是我都懒得喷那些喷 if err 的人罢了
    voidmnwzp
        23
    voidmnwzp  
    OP
       2023-04-28 18:36:28 +08:00 via iPhone
    elechi
        24
    elechi  
       2023-04-28 18:46:20 +08:00
    好像有个 multierr 包
    RedisMasterNode
        25
    RedisMasterNode  
       2023-04-28 20:35:29 +08:00
    觉得 if err != nil 让别人更容易搞懂程序每一步的错误想怎么处理...不明白为何要把它 warp 或者简化起来
    ck65
        26
    ck65  
       2023-04-28 20:39:36 +08:00
    字面意义上的过度封装。
    flyqie
        27
    flyqie  
       2023-04-28 21:02:47 +08:00
    hsfzxjy
        28
    hsfzxjy  
       2023-04-28 21:05:26 +08:00 via Android
    虽然但是,为什么是 warp 不是 wrap
    cmdOptionKana
        29
    cmdOptionKana  
       2023-04-28 21:14:19 +08:00
    我也有做类似的处理

    // WrapErrors 把多个错误合并为一个错误.
    func WrapErrors(allErrors ...error) (wrapped error) {
    for _, err := range allErrors {
    if err != nil {
    if wrapped == nil {
    wrapped = err
    } else {
    wrapped = fmt.Errorf("%w | %w", wrapped, err)
    }
    }
    }
    return
    }
    cmdOptionKana
        30
    cmdOptionKana  
       2023-04-28 21:16:46 +08:00
    我这个使用时应该更直白简洁, 例如

    a, err1 := func1()
    b, err2 := func2()

    if err := WrapErrors(err1, err2); err != nil { return err }
    zbinlin
        31
    zbinlin  
       2023-04-28 21:40:33 +08:00   ❤️ 1
    这时候不应该换语言了吗 🐶
    SenLief
        32
    SenLief  
       2023-04-28 22:06:19 +08:00   ❤️ 1
    if err 都没有,那还是 go 了吗?
    piaodazhu
        33
    piaodazhu  
       2023-04-28 22:26:57 +08:00
    想到个这个,大家看这样有什么问题不:
    1. 定义一个函数 func checkError(err error),函数内检查如果 err!=nil 就 panic(err)
    2. 在你业务函数开头写一个 defer func()内部做 recover()
    3. 在你业务函数中每次调用后,调用 checkError(err)

    这样就不用写那么多`if err != nil { ... `了吧,panic 被捕获时,也能知道发生 err 的是啥。
    yazinnnn
        34
    yazinnnn  
       2023-04-28 23:02:45 +08:00 via Android   ❤️ 2
    是我第 2887 喜欢的 go boy 重新发明 monad 时间🤗
    Kisesy
        35
    Kisesy  
       2023-04-28 23:08:13 +08:00
    @cmdOptionKana 楼主要的效果是,有函数有错误,下面的代码就不执行了,你这个即使 func1 出错了,func2 还是会执行
    voidmnwzp
        36
    voidmnwzp  
    OP
       2023-04-28 23:31:36 +08:00
    @piaodazhu 可以是可以 这样就是类似 java 的 try/catch 但是,风险太大 万一哪个地方漏写了 recover 那不是单个 goruntine down 了 是个整个进程都得挂 一不小心就是线上重大事故了属于
    voidmnwzp
        37
    voidmnwzp  
    OP
       2023-04-29 00:00:54 +08:00 via iPhone
    @hsfzxjy 因为我拼错了。z
    yrj
        38
    yrj  
       2023-04-29 05:45:16 +08:00
    @voidmnwzp 没事,我也经常拼错🤣
    Nasei
        39
    Nasei  
       2023-04-29 09:21:20 +08:00
    @magicdawn 如果不用库,rust 的 ? 在 Err 类型不一样的时候感觉很难受....
    magicdawn
        40
    magicdawn  
       2023-04-29 17:21:49 +08:00
    @Nasei

    有编译器语法糖啊, Result<>? / Option<>? 会自动 check 是否是 err / 是否有值
    不用库啊, go 可以学一下.
    magicdawn
        41
    magicdawn  
       2023-04-29 17:43:06 +08:00
    @Nasei map_err / ok_or_else 啊
    magicdawn
        42
    magicdawn  
       2023-04-29 17:47:10 +08:00
    @Nasei

    类型不一样这个...再怎么难受也比 go 机械地写 if err != nil 好吧, 本该是编译器做的事情却让开发者承担.
    voidmnwzp
        43
    voidmnwzp  
    OP
       2023-04-29 18:12:32 +08:00 via iPhone   ❤️ 1
    @magicdawn go2 会有语法糖的
    比如
    usr:=getUser
    handle err{
    return user{}
    }
    fregie
        44
    fregie  
       2023-04-30 10:12:11 +08:00 via Android
    golang 都迭了这么多代了,仍然一点没改错误处理机制,大概谷歌那帮开发 golang 的专家没有这里的评论者考虑的周全吧,为什么不加 try-catch 呢?
    从对 golang 的理解就可以看出各位到底是一位优秀的 coder 还是一位优秀的 engineer
    coder 会从自己的角度出发,觉得写起来最方便最快的就是好的
    engineer 会从工程的角度出发,考虑如何能让工程的效率和质量更好
    现代大型软件的业务逻辑 bug ,绝大多数都是因为错误处理不得当导致的,各位 coder 不妨回想一下最近一年遇到的 bug ,是不是如此。
    这种又臭又长的错误处理机制,虽然烦人,但是有效
    fregie
        45
    fregie  
       2023-04-30 10:14:36 +08:00 via Android
    @voidmnwzp 我打赌没有,有这种语法糖 go 就废了
    voidmnwzp
        46
    voidmnwzp  
    OP
       2023-04-30 15:17:12 +08:00 via iPhone
    @fregie 我认为 error is value 的设计并无不妥 只是实际业务中每一个 err 都需要去 if err 处理 太麻烦 不处理万一出现问题 那是找死都找不出来的
    ireina
        47
    ireina  
       2023-05-26 02:06:48 +08:00
    我也尝试过类似的事。我觉得没必要去改`if err != nil`其实,错误处理是一种计算效应,真正的干净的代码需要 monad ,但是目前没几个语言直接支持 monad 的上下文管理。把 error wrap 起来的确是一种处理错误的方式,标准库里其实也有使用过(但是很少),这种方式的缺点在于你必须很清楚 error 的状态是如何共享的。我觉得在给 interface 写 adapter 的时候可以用,但是平时写 struct 是不必要的。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2034 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 00:28 · PVG 08:28 · LAX 16:28 · JFK 19:28
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.