V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
SuperMild
5.49D

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

  •  
  •   SuperMild ·
    ahui2016 · Mar 6, 2023 · 7803 views
    This topic created in 1156 days ago, the information mentioned may be changed or developed.

    有些错误不需要特殊处理,就能用这些简单方便的函数,有错误就 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())
    }
    
    Supplement 1  ·  Mar 6, 2023
    刚刚才发现这个库 https://github.com/samber/lo 已经有一大堆利用泛型的常用函数
    46 replies    2024-02-06 08:52:19 +08:00
    sunny1688
        1
    sunny1688  
       Mar 6, 2023
    比 interface{}强一点
    cnbattle
        2
    cnbattle  
       Mar 6, 2023
    不喜欢 panic 0.0
    Nazz
        3
    Nazz  
       Mar 6, 2023
    看起来还不错
    rrfeng
        4
    rrfeng  
       Mar 6, 2023   ❤️ 1
    但是大部分时候不想 panic ,还是得造个 Option 好用
    PlG5sBkXD1ziLeGB
        5
    PlG5sBkXD1ziLeGB  
       Mar 6, 2023 via iPhone   ❤️ 4
    写过实际业务没大哥,线上也直接 panic 吗
    yzbythesea
        6
    yzbythesea  
       Mar 6, 2023
    panic 是高手
    voidmnwzp
        7
    voidmnwzp  
       Mar 6, 2023   ❤️ 2
    又在玩 java try catch 那套是吧 建议去写 Java
    fengjianxinghun
        8
    fengjianxinghun  
       Mar 6, 2023
    。。。。any 不就是 interface{}别名么。。
    chiuan
        9
    chiuan  
       Mar 6, 2023
    又把多态那些搞进来了吗?
    lance6716
        10
    lance6716  
       Mar 6, 2023 via Android   ❤️ 1
    一般叫 must ,你这个跟 try 有一毛钱关系吗
    SuperMild
        11
    SuperMild  
    OP
       Mar 6, 2023
    @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  
       Mar 6, 2023
    @fengjianxinghun interface{}要转类型,泛型不用
    rrfeng
        13
    rrfeng  
       Mar 6, 2023   ❤️ 1
    楼上说的对,Go 里会写一个 MustXXX() 用来 panic ,比你这个 Try 好。

    本身也就是多几行而已

    func XXX(){}
    func MustXXX(){
    if XXX() != nil { panic() }
    }
    SuperMild
        14
    SuperMild  
    OP
       Mar 6, 2023
    @fengjianxinghun 泛型是天生类型安全的,interface{}要自己写一堆判断才能确保类型安全。
    SuperMild
        15
    SuperMild  
    OP
       Mar 6, 2023
    @lance6716
    @rrfeng

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

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

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

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

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

    注意两点:

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

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

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

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

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

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

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

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

    (3) 如果各个开源库都有一套自己的 error 处理方式, 那么在使用的时候会不会造成障碍, 会不会额外产生一些心智负担
    SuperMild
        40
    SuperMild  
    OP
       Mar 7, 2023   ❤️ 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
       Mar 7, 2023
    @dragonsunmoon 我一直很敬佩 Rob Pike

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

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

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

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

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