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

想为 golang 添加一个错误处理语法糖 !err, 大家来看看这种错误处理方式如何

  •  
  •   sofukwird · 2023-08-31 08:01:18 +08:00 · 4091 次点击
    这是一个创建于 450 天前的主题,其中的信息可能已经有所发展或是发生改变。

    简介

    这是一个为 Go 添加 !err 错误处理语法糖的项目, 与 Go 完全兼容(毕竟只是语法糖)

    有了它你可以写如下代码, 代码更加紧凑 阅读更轻松

    转换前:

    package main
    
    import (
      "fmt"
      "os"
    )
    
    func main() {
      body, !err := readSelf()
      fmt.Println("main.go content")
      fmt.Println(body)
    }
    
    func readSelf() (content string, err error) {
      body, !err := os.ReadFile("main.go")
      content = string(body)
      return
    }
    
    

    转换后:

    package main
    
    import (
      "fmt"
      "os"
    )
    
    func main() {
      body, err := readSelf()
      if err != nil {
        return
      }
      fmt.Println("main.go content")
      fmt.Println(body)
    }
    
    func readSelf() (content string, err error) {
      body, err := os.ReadFile("main.go")
      if err != nil {
        return
      }
      content = string(body)
      return
    }
    
    

    项目地址: https://github.com/gone-lang/gone

    先来问问各位大佬的意见, 看下怎么做比较好

    第 1 条附言  ·  2023-08-31 09:00:36 +08:00

    打印错误日志.

    func readSelf() (content string, err error) {
    	defer func() {
    		if err == nil {
    			return
    		}
    		slog.Error("wrong", "err", err)
    	}()
    	body, ierr := os.ReadFile("main.go")
    	content = string(body)
    	return
    }
    
    

    如果需要立即处理错误, 可以不使用语法糖使用原有的 if err != nil {} 模式.
    语法糖是可以随时退出不使用的

    36 条回复    2023-09-01 09:41:32 +08:00
    AKAUP
        1
    AKAUP  
       2023-08-31 08:19:24 +08:00   ❤️ 1
    照你的方法,如果在报错的想打印日志应该怎么做呢,比如:
    ```
    if err != nil {
    logger.Error("something wrong")
    return err
    }
    ```
    morri
        2
    morri  
       2023-08-31 08:24:56 +08:00
    个人感觉其实 没必要每个错误都去处理,关键点判断好,单元测试写好,应该就可以了。

    例如

    var u entity.User
    _=dao.User.Ctx(ctx).Scan(&u,"id",uid)

    if u.id==0{
    return fmt.error("user does not exist")
    }

    // 这里直接返回自定义的错误,中间件也好其他处理,比如多语言返回
    rekulas
        3
    rekulas  
       2023-08-31 08:29:50 +08:00
    if err != nil {
    return
    }
    好家伙 返回值都不要了?
    rekulas
        4
    rekulas  
       2023-08-31 08:31:43 +08:00
    @rekulas 看错了 有返回值
    不过仍然感觉不是太好用,有些情况返回 err 但其他返回值可能也有用 意义可能不是很大
    rekulas
        5
    rekulas  
       2023-08-31 08:37:29 +08:00   ❤️ 1
    有个大问题是带来了兼容性问题, 我喜欢 go 的就是一次编码到处运行,如果用这种可能就得变成一次编码到处配置环境..用户下载一份源码,发现还无法编译得单独配置下,妥妥有前些年好压的既视感了,除非官方支持
    sofukwird
        6
    sofukwird  
    OP
       2023-08-31 08:55:43 +08:00
    @AKAUP 使用 defer 打印错误, 如果需要立即处理错误, 可以不是用语法糖使用原有的 `if err != nil {}` 模式. 语法糖是可以随时退出不使用的
    ```go
    func readSelf2() (content string, err error) {
    defer func() {
    slog.Error("wrong", "err", err)
    }()
    body, !err := os.ReadFile("main.go")
    content = string(body)
    return
    }

    ```

    @rekulas 并不会出现兼容性问题, 会另外生成一个转译后 go 文件, 与其他 go 库是无缝操作的, 你可以认为是 typescript 这种
    @morri 其实你这里也是进行了一个错误处理的判断
    lisxour
        7
    lisxour  
       2023-08-31 09:00:54 +08:00   ❤️ 1
    if err != nil { 不带其他逻辑直接 return }

    一个语法糖只为了对这么一个特定场景下少两三行代码,大概率不会给你过的
    proxychains
        8
    proxychains  
       2023-08-31 09:02:05 +08:00
    @morri 头像好评
    cin
        9
    cin  
       2023-08-31 09:08:09 +08:00   ❤️ 4
    sofukwird
        10
    sofukwird  
    OP
       2023-08-31 09:08:26 +08:00
    @lisxour 正是如此, 已经是被拒绝了所以另起炉灶来实现自己的需求
    SingeeKing
        11
    SingeeKing  
       2023-08-31 09:12:01 +08:00
    我很好奇附言里是怎么把 !err 打成 ierr 的…… 难道 lz 用的手写输入?
    sofukwird
        12
    sofukwird  
    OP
       2023-08-31 09:12:16 +08:00
    @cin golang 该如何调用 go+ 代码库呢?
    sofukwird
        13
    sofukwird  
    OP
       2023-08-31 09:26:27 +08:00
    @SingeeKing 这个是一个已有的转译实现, 我用了 `ierr` 作为 `!err` 的替代, 但遇到了一些问题, 打断点需要到转译后的文件才能打上, 在开发中还蛮影响思路的, 后面想到了如果做成 golang 超集语言的话就可以打上断点, 于是又起了个仓库准备实现新思路

    已有的转译实现: https://github.com/shynome/err4 , 现在正在用

    (就像前段时间的帖子说的, 每个程序员都有改造语言的冲动, 我也是对 golang error 的错误处理不满意所以就有这么一个个尝试
    lanlanye
        14
    lanlanye  
       2023-08-31 09:28:19 +08:00 via iPhone   ❤️ 4
    真要做的话我觉得 rust 的方式就不错,语句末尾加一个问号。
    githmb
        15
    githmb  
       2023-08-31 10:03:30 +08:00
    Rust:
    let Ok(bar) = foo() else {
    return fuckyou
    }
    ganbuliao
        16
    ganbuliao  
       2023-08-31 10:23:46 +08:00
    按照 go 的 习惯 是不会加新的语法糖的 毕竟 go 写出来的代码 主要是为了 review 的人 看着舒服的
    vchroc
        17
    vchroc  
       2023-08-31 10:25:25 +08:00
    语言肯定朝着越来越好用的方向发展。
    错误处理,目前看比较好用的有两种:
    GOTO:Exception/Panic
    正确错误二元组:Result<T, E>( Rust )

    golang err 绝对不是正确的方向
    neoblackcap
        18
    neoblackcap  
       2023-08-31 10:27:03 +08:00
    @lanlanye 有类型系统兜底呢,不是简单语法改改就能模仿
    fioncat
        19
    fioncat  
       2023-08-31 10:31:05 +08:00
    像 Rust 那样整个?作为语法糖?

    err := handle()
    if err != nil {
    return fmt.Errorf("Handle error: %w", err)
    }

    ===>

    handle()?.Errorf("Handle error")
    mcfog
        20
    mcfog  
       2023-08-31 10:51:30 +08:00
    https://github.com/golang/go/issues/32437 这里有最全的 golang error handle 的意见
    learningman
        21
    learningman  
       2023-08-31 10:53:24 +08:00 via Android
    你这样不如
    v, ? := xxx()
    guonaihong
        22
    guonaihong  
       2023-08-31 11:16:44 +08:00
    现在一堆 ai 辅助编程的插件 感觉收益不是那么明显。copilot 一个回车解决的事情。
    codersdp1
        23
    codersdp1  
       2023-08-31 11:46:19 +08:00
    func readSelf() (string, error) {
    body, err := os.ReadFile("main.go")
    if err != nil {
    return "",err
    }
    content = string(body)
    return content,nil
    }

    这种怎么处理 🤔️
    PTLin
        24
    PTLin  
       2023-08-31 12:32:03 +08:00
    https://github.com/golang/go/issues?q=label%3Aerror-handling
    看看这些数不清的提案,相信总有一个是比你这个想法优秀且被枪毙了的。
    sofukwird
        25
    sofukwird  
    OP
       2023-08-31 13:09:05 +08:00 via Android
    @codersdp1 #23 这种情况不使用语法糖,直接使用你现在使用的代码,这个糖是有限的,不打算应对所有情况

    @guonaihong 主要还是想阅读的时候更简单轻松,虽然编辑器能补全但代码行数是实打实地变多了

    @fioncat 不选择 v := xxx()? 是因为它假定了最后返回 error ,而且在只返回 error 的情况下可读性并不好,需要看到末尾才知道这行是否可能出错

    xxx()?

    @learningman 不选择 v, ? := xxx() 则是因为转译后需要自己生成 err 临时变量名并返回,临时变量名会千奇百怪的导致可读性下降。那使用 v, ?err := xxx() ?我一开始就是提议的这种后来发现 v, !err := xxx() 这种可读性更好。

    我选择这种模式是因为它易于实现,又或者说正是因为它易于实现我才能选择它
    raies
        26
    raies  
       2023-08-31 13:35:56 +08:00
    不如这样

    body := readSelf()?
    ck65
        27
    ck65  
       2023-08-31 13:48:51 +08:00   ❤️ 1
    感觉无糖更方便,因为我已经告诉别人我是 Errlang 开发者了。
    index90
        28
    index90  
       2023-08-31 16:16:28 +08:00
    FP 才是最优解:
    type Result[T any] struct { v T; err error }
    func(r *Result[T]) IfErr(f func(error)) { if r.err != nil {f(err)} }
    func fmap[A, B any](ra Result[A], f f(a) Result[B]) Result[B] {
    if ra.err != nil { return f(ra.v) }
    return Result[B]{err: error}
    }
    liuguang
        29
    liuguang  
       2023-08-31 16:23:48 +08:00
    设计上用元组就是一个大的失误,值和错误不会同时出现。
    而 rust 的枚举就是很好的设计,rust 的问号运算符也是一个好的语法糖。
    mcfog
        30
    mcfog  
       2023-08-31 16:59:48 +08:00
    @liuguang 偶尔会同时出现的,例如读写 socket 、文件可以失败的同时已经成功了一部分

    不过我同意还是支持代数数据类型能解决不少问题
    Mohanson
        31
    Mohanson  
       2023-08-31 18:25:01 +08:00   ❤️ 1
    对于编程语言来说, 除非你有充分的理由, 否则不要加语法糖.

    注: 我觉得这样写很 cool 就不是一个充分的理由.
    mainjzb
        32
    mainjzb  
       2023-08-31 18:46:01 +08:00
    https://github.com/golang/go/issues/62253
    你的想法已经被毙过了
    aduo
        33
    aduo  
       2023-08-31 19:24:53 +08:00
    想法挺好的... 但是没有明确的 return 语句却可能会 return ,有点孩怕, 感觉容易出事故
    sofukwird
        34
    sofukwird  
    OP
       2023-08-31 20:13:46 +08:00 via Android
    @ck65 golang 称为 err-lang 也确实是实至名归,1/5 的代码行数是由 if err != nil {} 提供的
    第一眼看成了 erlang 略过去了

    @mainjzb 拒绝的理由是我们已经拒绝过了这项提案,泛型也是被拒绝过的现在也加上去了。
    正如 issue 里提到的回复:“golang 不会根据各种错误处理提案的喜爱程度对错误处理进行改进,golang 有自己的节奏”

    @aduo panic 也会导致函数提前返回,相对于使用 panic 的错误处理方案 !err 不会有性能损失
    joesonw
        35
    joesonw  
       2023-08-31 22:24:03 +08:00 via iPhone
    最关键的问题是,一般是鼓励 wrap 一下错误,不然你只看到 io 错误,根本不知道是哪里的问题,排查起来头痛。
    mainjzb
        36
    mainjzb  
       2023-09-01 09:41:32 +08:00   ❤️ 1
    我也会拒绝你,你这等于给语言加了一个宏仅仅为了偷懒少写一些代码而已。
    当然 go 的初期设计确实考虑的很简陋,更聪明一点的做法是像 zig 或 rust 那样。
    go error 设计更大的问题是没有保存堆栈。对于仅仅多写一点代码的问题,完全可以用 ide 来自动补充来解决。
    无论是你的设计还是 rust 的设计还是 zig 的设计,还是当前 go 的设计,都不影响阅读代码。 写代码只是为了偷懒,我认为没有完全的必要性添加此项更改。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2711 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 09:57 · PVG 17:57 · LAX 01:57 · JFK 04:57
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.