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

有了泛型,以前一直想做的 Go 错误处理终于可以实现了

  •  
  •   SuperMild ·
    ahui2016 · 2023-03-06 10:28:59 +08:00 · 6696 次点击
    这是一个创建于 628 天前的主题,其中的信息可能已经有所发展或是发生改变。

    有些错误不需要特殊处理,就能用这些简单方便的函数,有错误就 panic, 无错误就直接返回有用结果:

    func Try(err error) {
    	if err != nil {
    		panic(err)
    	}
    }
    
    func Try1[V any](val V, err error) V {
    	Try(err)
    	return val
    }
    
    func Try2[V1 any, V2 any](val1 V1, val2 V2, err error) (V1, V2) {
    	Try(err)
    	return val1, val2
    }
    

    一个例子:

    // 不使用泛型的传统实现
    func GetExePath() string {
    	path, err := os.Executable()
    	if err != nil {
    		panic(err)
    	}
    	return path
    }
    
    // 使用泛型,告别 `if err != nil`
    func GetExePath() string {
    	return Try1(os.Executable())
    }
    
    第 1 条附言  ·  2023-03-06 11:00:12 +08:00
    刚刚才发现这个库 https://github.com/samber/lo 已经有一大堆利用泛型的常用函数
    46 条回复    2024-02-06 08:52:19 +08:00
    sunny1688
        1
    sunny1688  
       2023-03-06 10:35:41 +08:00
    比 interface{}强一点
    cnbattle
        2
    cnbattle  
       2023-03-06 10:36:24 +08:00
    不喜欢 panic 0.0
    Nazz
        3
    Nazz  
       2023-03-06 10:38:00 +08:00
    看起来还不错
    rrfeng
        4
    rrfeng  
       2023-03-06 10:38:41 +08:00   ❤️ 1
    但是大部分时候不想 panic ,还是得造个 Option 好用
    yyf1234
        5
    yyf1234  
       2023-03-06 10:41:48 +08:00 via iPhone   ❤️ 4
    写过实际业务没大哥,线上也直接 panic 吗
    yzbythesea
        6
    yzbythesea  
       2023-03-06 10:42:41 +08:00
    panic 是高手
    voidmnwzp
        7
    voidmnwzp  
       2023-03-06 10:44:17 +08:00   ❤️ 2
    又在玩 java try catch 那套是吧 建议去写 Java
    fengjianxinghun
        8
    fengjianxinghun  
       2023-03-06 10:44:42 +08:00
    。。。。any 不就是 interface{}别名么。。
    chiuan
        9
    chiuan  
       2023-03-06 10:46:45 +08:00
    又把多态那些搞进来了吗?
    lance6716
        10
    lance6716  
       2023-03-06 10:47:03 +08:00 via Android   ❤️ 1
    一般叫 must ,你这个跟 try 有一毛钱关系吗
    SuperMild
        11
    SuperMild  
    OP
       2023-03-06 10:48:40 +08:00
    @yyf1234 有使用场景的,你看看这个很流行的库也提供了这个函数 https://github.com/samber/lo#must

    Must
    Wraps a function call to panics if second argument is error or false, returns the value otherwise.
    sunny1688
        12
    sunny1688  
       2023-03-06 10:50:08 +08:00
    @fengjianxinghun interface{}要转类型,泛型不用
    rrfeng
        13
    rrfeng  
       2023-03-06 10:50:16 +08:00   ❤️ 1
    楼上说的对,Go 里会写一个 MustXXX() 用来 panic ,比你这个 Try 好。

    本身也就是多几行而已

    func XXX(){}
    func MustXXX(){
    if XXX() != nil { panic() }
    }
    SuperMild
        14
    SuperMild  
    OP
       2023-03-06 10:51:06 +08:00
    @fengjianxinghun 泛型是天生类型安全的,interface{}要自己写一堆判断才能确保类型安全。
    SuperMild
        15
    SuperMild  
    OP
       2023-03-06 10:52:58 +08:00
    @lance6716
    @rrfeng

    确实,我这个函数名搞错了。原本我用的函数名是 Panic, 但看着太让人紧张于是改成 Try 。然后刚刚才知道有 lo 这个库。
    dw2693734d
        16
    dw2693734d  
       2023-03-06 10:59:55 +08:00
    @yyf1234 我就是线上 panic😂,受了 erlang 的 let it crash 的影响
    yyf1234
        17
    yyf1234  
       2023-03-06 11:09:17 +08:00 via iPhone
    @dw2693734d
    @SuperMild
    MustXXX() 一般在启动时检查配置用吧,业务里面可别 panic
    SuperMild
        18
    SuperMild  
    OP
       2023-03-06 11:15:14 +08:00
    @yyf1234 对。

    另外有时候时间紧或者写 demo 的时候就先 panic 偷懒,后续再好好处理错误,反正有 web 框架兜底,程序不会真的崩,向前端返回 500 而已。
    wtfedc
        19
    wtfedc  
       2023-03-06 11:38:57 +08:00
    500 和程序崩也一样,nginx 也是返回 502
    Jaron0608
        20
    Jaron0608  
       2023-03-06 12:15:15 +08:00 via Android
    @wtfedc 这里的程序崩是说整个进程退出,影响了其他接口的正常使用
    tangMu
        21
    tangMu  
       2023-03-06 12:29:20 +08:00
    然而,代码也越来越难读了
    QlanQ
        22
    QlanQ  
       2023-03-06 13:07:17 +08:00
    这样写,PHP 不香吗?

    要么 PHP 随便抛,要么 rust ,必须 处理
    gitxuzan
        23
    gitxuzan  
       2023-03-06 13:15:57 +08:00
    还有你定义错误的行数都不清楚,还是老老实实写吧,到了线上不是方便了,是找麻烦
    darksword21
        24
    darksword21  
       2023-03-06 13:36:26 +08:00
    我不是很理解
    raynor2011
        25
    raynor2011  
       2023-03-06 16:51:42 +08:00   ❤️ 1
    https://github.com/samber/mo 这个库更符合 lz 的想法,这是一种函数式编程的设计模式,https://en.wikipedia.org/wiki/Monad_(functional_programming)
    qza1212
        26
    qza1212  
       2023-03-06 17:13:05 +08:00
    这个就是装饰器啊,不推荐在装饰器里做异常处理,这样会导致装饰器有隐藏的顺序要求
    lesismal
        27
    lesismal  
       2023-03-06 17:50:23 +08:00
    转 go 这些人哪,入乡随俗了解一下,非要越搞越蹩脚。。。
    securityCoding
        28
    securityCoding  
       2023-03-06 21:01:06 +08:00
    你是个高手 233 ,go 代码 panic oncall 工单不得搞死你
    Hanggi
        29
    Hanggi  
       2023-03-06 21:09:42 +08:00
    想了解下楼主用 Go 的原因是啥,明明思想上无法接受。

    就好像手里攥着圣经敲木鱼一样,怎么看都是别扭
    iseki
        30
    iseki  
       2023-03-06 21:21:08 +08:00 via Android
    Go 的整个设计都让搞 try catch 极其困难
    我只会在业务逻辑里 panic 而不 recover ,以 panic 为中断整个处理的方式
    SuperMild
        31
    SuperMild  
    OP
       2023-03-06 22:18:15 +08:00
    @Hanggi 我也很好奇,你为什么说我无法接受 Go 的语法?

    if err != nil {panic(err)} 能处理错误,Must1(val, err) 也能处理错误,都符合 Go 的语法。

    注意两点:

    1. 看我的正文第一句话 “有些错误不需要特殊处理,就能用这些简单方便的函数”,我并没有说每一个错误都这样处理,只是有时候能偷懒,就这样偷懒,比如程序初始化阶段的一些很应该出错就崩溃的错误。

    2. 我记得 Go 语言之父说过,error 就是一个普通的值,他建议大家用自己的方法写一些函数来处理这个值,怎么方便怎么来。
    SuperMild
        32
    SuperMild  
    OP
       2023-03-06 22:24:36 +08:00
    我不理解,上面很多人很鄙视用 panic 处理错误,意思是

    A. 只要用了 panic, 就该被嘲笑,Go 语言就不该有 panic 这个函数。
    B. 如果要用 panic, 就只能这样用 if err != nil {panic(err)},其他方法比如包裹一层 Must1(val, err) 就不行

    究竟是 A 、B 哪个意思?
    CC11001100
        33
    CC11001100  
       2023-03-07 00:02:44 +08:00   ❤️ 1
    @SuperMild 生产环境中确实应该尽量避免 panic ,太容易背锅,用户可不 care let it crash 这一套,他只知道这玩意儿不能用了就投诉,完了就得背一个 P0 故障,锅背多了就得走人了。。。
    SuperMild
        34
    SuperMild  
    OP
       2023-03-07 00:07:50 +08:00
    @CC11001100 对呀,我也没说无脑一律 panic ,我第一句话说的就是 “有些错误不需要特殊处理,就能用这些简单方便的函数”,自己看情况用,另外我也附言给出了一个流行库,里面除了有 Must, 还有 TryOr 等多种不同的处理错误的方式,各有不同的使用场景。
    dragonsunmoon
        35
    dragonsunmoon  
       2023-03-07 00:21:25 +08:00
    唉, 大道至简, go 的抽象表达能力就只能这样, 老老实实的一行代码,三行错误处理得了, 别整些其他语言的特性, 整出来的也是不伦不类.
    SuperMild
        36
    SuperMild  
    OP
       2023-03-07 00:33:07 +08:00
    @dragonsunmoon

    以前没有泛型没办法,现在原本三行错误处理可以轻松简化为一行,为什么要“老老实实”?

    也没有不伦不类啊,用的都是 Go 的最最基本的语法,仅仅非常简单地包裹了一层而已,这种包裹一下变成一个方便的函数的做法,不是日常编程的常规操作吗?
    xuanbg
        37
    xuanbg  
       2023-03-07 02:37:43 +08:00
    不要用 try catch 替代 if 来进行数据校验呀。try catch 也好,panic 也罢,这个习惯很不好的。
    SuperMild
        38
    SuperMild  
    OP
       2023-03-07 08:41:38 +08:00
    各位,不要执着于是否使用 panic, 重点应该是现在有了泛型,有些错误处理可以简化了,panic 只是其中一个例子而已,上面给出了 lo 和 mo 两个库,里面有 TryOr, Option 等多种方便的函数,根据需要选用。
    dragonsunmoon
        39
    dragonsunmoon  
       2023-03-07 11:01:25 +08:00
    @SuperMild
    探讨一些啊, 有几个问题

    (1) go 官方给出的 error 处理的最佳实践是啥样的

    (2) go 官方给出的泛型编程的最佳实践是啥样的

    (3) 如果各个开源库都有一套自己的 error 处理方式, 那么在使用的时候会不会造成障碍, 会不会额外产生一些心智负担
    SuperMild
        40
    SuperMild  
    OP
       2023-03-07 11:24:15 +08:00   ❤️ 1
    @dragonsunmoon 刚好我也想更详细一点说说这个问题。

    上面我提到 “我记得 Go 语言之父说过,error 就是一个普通的值,他建议大家用自己的方法写一些函数来处理这个值,怎么方便怎么来”

    凭着记忆,我找到了来源。

    先看这篇发表在 Go 官方博客,Rob Pike 写的文章 https://go.dev/blog/errors-are-values

    拉到文章最后,他总结道:

    > Use the language to simplify your error handling.
    > But remember: Whatever you do, always check your errors!

    意思是,建议大家灵活使用 Go 语言去简化错误处理,只要别漏掉错误就行。

    ===============

    然后他推荐了一篇文章 https://jxck.hatenablog.com/entry/golang-error-handling-lesson-by-rob-pike

    这篇文章是日英双语的,讲述了一个活动上,博客作者 jxck 对 Go 的错误处理有疑惑,受到 Rob Pike 指导的过程。

    jxck 遇到了需要写大量 if err != nil {return err} 的情况,向 Rob Pike 请教,Rob Pike 当场就给他写了一个 Wrapper, 也就是包裹了一层,把 error 先集中记录下来,后续再一次性处理。

    ===============

    因此,Rob Pike 写了那篇官方博客,标题就是 Errors are values ,意思是不要把错误处理看成什么特殊的事情,error 就是一个普通的值,你如何对待别的任何数值、变量,就如何对待 error, 大胆去用常规编程技巧处理它。
    SuperMild
        41
    SuperMild  
    OP
       2023-03-07 11:42:57 +08:00
    @dragonsunmoon 我一直很敬佩 Rob Pike

    他超过 65 岁了,但他毫不墨守成规,他敢反主流。他不会问主流是什么,如果大家不符合主流会不会沟通困难,他设计的 Go 语言就极大胆地反主流,一个新语言,静态类型的,敢没有 try-catch 处理异常,没有泛型,敢加进指针,没有标准的面向对象,而是用隐性接口,这一大堆设计都是非常大胆的。

    按照上面很多人的说法,完全可以说 Go 语言本身就有一大堆不伦不类的设计。但是,为什么要这样想问题呢,连 60 多岁的老人都敢勇于“标新立异”。

    ============

    题外话,Go 官方网站的文档 pkg.go.dev 是当今极罕见,代码不带语法高亮的,但是我们也许并未感觉有啥特别不方便。因为 Rob Pike 发现,语法高亮其实没啥用。

    我自己的体验是,写代码时语法高亮有点帮助,但阅读代码时,一旦沉浸进去,是绝对感受不到有没有高亮的区别的。
    fds
        42
    fds  
       2023-03-07 13:52:39 +08:00
    op 推荐的 https://github.com/samber/do 这个 DI 库看起来不错,找机会试用下,少写点儿全局变量。
    standchan
        43
    standchan  
       2023-03-16 10:21:44 +08:00
    panic 没啥问题,但是一般我们都是在程序初始化的过程中如果发生问题就直接 panic 掉,比如数据目录的创建失败之类的,在程序运行过程中是极少 panic 的(几乎没有)你写这个用起来舒服,但是 log 报错的代码行数都是 Try 函数的第三行吧,这个就很麻烦。
    standchan
        44
    standchan  
       2023-03-16 10:23:36 +08:00   ❤️ 1
    @SuperMild 你把错误丢给一个函数去处理,然后报错的时候,你定位不到具体代码行,这个想过吗
    SuperMild
        45
    SuperMild  
    OP
       2023-03-16 16:59:05 +08:00
    @standchan 你看我正文第一句话 “有些错误不需要特殊处理”,如果我没想过这个函数的使用场景有限,我又怎么会说这句话呢。
    libinglong9
        46
    libinglong9  
       291 天前 via iPhone
    还是用 nodejs 吧
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2430 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 16:01 · PVG 00:01 · LAX 08:01 · JFK 11:01
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.