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

要学 Go 的赶紧上车

  •  
  •   kidlj ·
    kidlj · 2020-10-03 12:59:17 +08:00 · 23846 次点击
    这是一个创建于 1306 天前的主题,其中的信息可能已经有所发展或是发生改变。

    Go 预计在 2021 年开始支持范型,那个时候代码对新手的复杂度将提升一个量级。当前 Go 语言只有一种明显的抽象,就是 interface (接口),范型是另一种,而且比接口要更难以理解和编写、阅读。

    2017 年我决定转 Go 的时候,调查了社区的成熟度,比如 kafka,redis,mongo,mysql,pg 等等中间件的库是否完善,那时候这些该有的都已经有了。现在又过了三年,Go 的社区更加成熟了,特别是 Docker 和 Kubernets 主导的云原生生态的崛起,Go 语言占了半壁江山还多。

    前一阵坐我背后的一个写 Python 的同事在学习 Go,问我一个简单的问题。一个函数的返回值是 error 类型,被他当作是返回变量,因此代码看不懂。这可能是只写过动态类型语言( python, javascript, php 等)都要面对的一个思维转变。这个时候学习 Go,除了静态类型的思维转变以及 interface 这一层抽象,你会发现 Go 大部分时候像一个动态类型的语言,而且特性非常少(比 Python 少得多),Go 开源代码和标准库也非常 accessible 。

    不过当范型推出来以后,虽然从 draft design 来看范型的设计已经非常简单,但对于没接触过静态类型语言的同学来说这是一个不小的挑战,甚至函数的签名都难以分辨。因此这是一个肉眼可见的复杂度提升。而且可以预计的是,当范型可用的时候,社区里大量的开源项目和库将迁移到范型的实现(因为代码更紧凑和通用),我觉得那个时候代码不会像当前一样 accessible 。

    所以这个时候上车,用大约半年到一年时间熟悉 Go 的静态类型和 interface,等到范型推出的时候,可以比较轻松地过渡过去。

    下面是一些学习资料的推荐:

    1. <The Go Programming Language> 是 Brian Kernighan 和一位 Go 开发组成员 Alan 合写的一本书。虽然这本书出来好几年了,但是 Go 从 1.0 以后就没怎么变化,所以还是很适用。推荐阅读英文版,中文版在大部分时候翻译得不错,但是在一些难以理解的部分,翻译并不能让事情更容易理解,甚至还会出现错误。
    2. Web 框架推荐 Echo 。写过 Node.js 的同学会发现,Echo(或 gin ) 就是 Express 或 Koa 的翻版,都是 middleware 和 c.Next 的执行方式,属于极简框架,很容易上手。
    3. Go 的并发很简单,只有 Goroutine 和 channel 一种方式。官方出的那本书里讲解得非常清晰,必读。不过一开始如果理解起来有困难的话,甚至可以跳过,因为很多时候不太用得着。比如用 Echo 框架来写业务,大部分时候不涉及并发,并发是在 Echo 框架的层面实现的(一个请求一个 goroutine ),所以业务代码就不需要并发了。
    4. Go modules 很好用。新推出不久的 pkg.go.dev 可以查询某个库被哪些开源项目 import,可以方便地学习这个库的使用方式。比如想看哪些开源项目使用了 Echo 框架,点开 Echo 的 import by tab 就看到了。这里是示例: https://pkg.go.dev/github.com/labstack/echo/v4?tab=importedby

    我写 Go 有两年时间,肯定不算是一个 Go 的高手,但也不害怕去阅读一些大型项目的代码,比如 Kubernetes 。这就是 Go 的魔力,very accessible 。就像上面说的,当范型被大量运用以后,难度应该要提高一个量级。这是我的一点点经验,分享给大家,希望有帮助。

    180 条回复    2020-11-26 10:39:41 +08:00
    1  2  
    Nugine0
        101
    Nugine0  
       2020-10-04 11:20:42 +08:00 via Android
    @reus 根据你的介绍,这个 io.Reader 显然是没有 union type 带来的设计失误。

    按正常人的思维,调用一个接口要么成功,要么失败,应该用 union type 。但 (42, io.EOF) 算什么?叠加态?

    你可以列很多理由说明这个设计合理,但改变不了它反人类的事实,它的语义和正常函数是不同的。这就是额外的心智负担,说好的大道至简呢?

    go 阅读起来也不是那么容易。检查完 err 不检查空指针,直接挂了。调用栈追下去,一个 interface{}拍脸上,得,全动态了。

    go 用看似简单的语法给人快速入门的错觉,真以为什么人都能看懂大项目?真以为换条赛道就能马上超车?路上的坑一个也逃不了,全要挨个踩过去。
    mywaiting
        102
    mywaiting  
       2020-10-04 11:37:37 +08:00
    世界上只有两种语言,一种没人用,另一种被喷成渣
    Nugine0
        103
    Nugine0  
       2020-10-04 12:02:27 +08:00 via Android
    @reus 再说 rust 。
    non_exhaustive 归 lint 管,上游新增了字段会被 lint 查出来,可以自动化。
    <https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_enum_match_arm>
    讲真的,不会有编程语言连这种程度的 lint 都做不到吧。

    有异步运行时能把同步 IO 转异步 IO,依赖的就是非阻塞和 WouldBlock 。
    Rust 异步运行时依靠社区,没 go 那么统一,某种程度上是弱点,但也给出了自由空间。
    reus
        104
    reus  
       2020-10-04 12:20:58 +08:00   ❤️ 4
    @Nugine0 能不能不要双标?你不会看前面有人给出的 rust 代码和我给出的 go 代码?
    rust 的 match 有四个 case,go 的 if 有三个。你要是说 go 这种设计反人类,那 rust 同样也反人类。
    正常函数?你自己不适应就叫不正常?
    要么成功要么失败才正常,那 Ok(0), Ok(n), Err(ErrorKind: Interrupted), Err(e),四种情况,符合你不反人类的标准吗?没有心智负担是吧?
    说好的 union type 呢?怎么还一个套一个的?怎么不用同一个 union type 表达所有情况?
    能不能不要双重标准?

    lint ?前面有人说了是语言机制,到你这里倒成了需要 linter 来做了?
    你要是认为 linter 也算是机制的一部分,那还有什么好谈论的? go 也有一堆 linter,前面说的什么忘记检查等等,全都可以提示。

    明明就是狭隘偏执,非要说得自己代表全人类,和自己熟悉的设计不同,就是反人类了,好大的脸!
    est
        105
    est  
       2020-10-04 12:44:01 +08:00
    golang 创始人口味和大多数 crud boy 的不符。
    reus
        106
    reus  
       2020-10-04 12:51:58 +08:00   ❤️ 2
    @Nugine0 一个函数可以返回多个值,你是缺乏这种思维。
    你缺乏的思维,就是不正常的思维?
    42, io.EOF 的意思是,读了 42 字节,并且已经到达字节流的末尾,不用再读,很难理解吗?
    换成单返回值,就需要两次调用,一次返回 42,一次返回 0 。我前面已经说过,go 这种设计可以节省一次调用。
    “要么成功,要么失败”,显然是错的,rust 的成功,也包含两种成功,一种表示 EOF,一种表示读了一些。rust 的失败,也不止一种失败,而且 Interrupted 是一种特殊的失败,它并不是真的失败,只是暂时的失败。
    io 本来就有固有的复杂度,不要以为抬出 union type 就是银弹,明明就不是。
    我不是说 rust 的设计有问题,rust 的设计反映了 io 本身的复杂。有问题的是你们这些人,无视事实,非要用不用标准,褒扬一个贬低另一个。
    Jirajine
        107
    Jirajine  
       2020-10-04 13:36:02 +08:00
    @reus 首先,这个 interrupt 是因为 rust 的标准库接口暴露出了更多的底层复杂度,和语言的设计没太大关系。
    如果用 tokio::io::AsyncReadExt 代替 std::io::Read 的话,就和 go 一样不需要考虑底层细节了。

    至于 union type,绝大多数使用情况下,当你调用完一个函数,应该成功时返回结果,失败时返回错误(原因)。当你检查完 if err!=nil 之后,一般情况是认为 既然 err 是 nil,那 val 就不是 nil,如果这个函数写的有问题的话,就可能同时返回(nil,nil),接下来你没有额外检查的话就会遭遇臭名昭著的"NullPointerException"。
    要是同时检查 val 和 err 是否为 nil 的话,那就太过 verbose 了。
    以及那个 check 语法糖也没实装,当嵌套调用函数 /方法的时候就得一遍遍的复制粘贴,这显然也是语言设计失败的地方。

    而 union type 则从语言层面确保了这一点,而不是仅靠程序员的约定俗成,只要返回 Ok(val),那 val 就能够确保不会是空。
    nullable 是 go 里很有问题的一个方面,使用 union type Option<T>也能够很优雅的解决这个问题。

    再说 error,go 的 error 类型也是比较残废的,你只知道它返回 error,却不知道可能返回哪些 error 。rust 除了 non_exhaustive 这种刻意让你 fallback 处理的情况外,是能保证你显式的处理(或显式的忽略)了每一种可能的错误,不会出现 unexpected error,与 Java 的 checked exception 有异曲同工之处。

    我这里说的糟粕,主要是指语言设计层面上。goroutine 则可以说是 go 最大的亮点,可惜 tokio 没法抄过来,async 带来了大量的复杂度,远没有 goroutine 写的爽快。
    reus
        108
    reus  
       2020-10-04 14:06:46 +08:00
    @Jirajine 你在说啥?以 io.Reader 为例,返回类型是 (int, error),哪来什么 null pointer ?
    就算返回的是指针,且函数有问题,那后面如果遇到解指针,那就会直接 panic,表明程序需要改,是不需要写代码做检查的。
    rust 你以为就没有问题?你看看多少人直接用 unwrap 吧,编译器可不会禁止你用。

    如果你认为需要一遍又一遍复制,那你不会写成函数吗?这么简单的减少代码的技巧都不会吗?

    说了半天你都没明白,union type 没法保证什么吗?
    明明 rust 返回的 Ok(val),就分成 val = 0 和 val > 0 两种情况。既然抬出 union type,怎么不定义一个类型,分开这两种情况? Ok(val) 分成 Ok(n) 和 Zero,这样编译器才知道你必须处理返回 0 的情况。问题就是,rust 不是这么设计的。明明就是需要约定俗成检查 0,明明就是约定俗成需要检查 Interrupted 。比 go 优雅在哪里?我看不到。
    你不处理 Ok(0),编译器报错吗?你不处理 Interrupted,编译器报错吗?

    go 里你不需要知道返回哪些 error,你只需要知道你能处理哪些 error,把你能处理的处理掉,不能处理掉的就直接返回,就行了。java 的 checked execption 不知道多少人批评,有什么好学的?

    例如一个函数,调用另一个函数,且知道可以处理 ErrFoo 和 ErrBar 类错误,其他不管,是这样写的
    err := fn()
    if e := ErrFoo{}; as(err, &e); {
    // 处理 ErrFoo 类错误
    } else if e := ErrBar{}; as(err, &e); {
    // 处理 ErrBar 类错误
    } else {
    // 不知道怎么处理,直接返回
    return err
    }

    处处都想要处理任何错误,本身就是个伪需求。
    chibupang
        109
    chibupang  
       2020-10-04 14:36:04 +08:00 via iPhone
    有什么好争的,语言就是工具而已,真不喜欢 xx 语言的某个特性你可以不用它,非要恶心自己干嘛呢?如果不得不用,那你就尝试去改变它呀。如果没本事决定自己用什么语言,又没能力去改变它,还恶心自己,那可能不是语言本身的问题了。( again,语言不过就是工具
    Jirajine
        110
    Jirajine  
       2020-10-04 15:07:30 +08:00
    @reus io 这里是比较特别的情况,你一般封装的函数不会出现正确和错误的叠加态。
    有问题是指会在某些 rare case 同时返回 nil,这种运行时错误难以排查,能够在编译时就解决当然要好的多。

    unwrap()也不是语言的问题吧,你用 go 的库代码要是出错不上抛无脑 panic()不也是坑么。

    每次拿函数 /方法返回值使用的时候都得一遍遍的复制 if err!=nil {return nil,err} 这怎么解决?

    当然一般情况不会处理每一种错误,但能和不能还是有区别的。像你这样把其他错误抛上去,那上层呢?你多抛几个来源不通的错误,上层没法知道有哪些错误他可以处理,再往上抛,这样层层叠加。最后你根本没办法区分,最终最外层只能包个 try catch 那样记录个日志然后退出。
    如王垠讲的那样 https://www.yinwang.org/blog-cn/2017/05/23/kotlin,checked exception 好处是毫无疑问的,而类型系统保证的 Error type 得到同样好处的同时却不像 chacked exception 那样在实践中有那么多问题。
    XIVN1987
        111
    XIVN1987  
       2020-10-04 15:35:42 +08:00   ❤️ 2
    我觉得 Go 相对于 Java 并没有不得了的优点
    都是带 GC 的静态语言,唯一重大的区别是 Java 执行需要虚拟机,而 Go 直接执行机器码,,不过对于桌面应用和服务器来说,几十 M 的虚拟机并不造成严重的劣势,,而且虚拟机本身也会使得一些程序语法更容易实现

    另外,说 Go 简单,,那是因为 Go 还年轻,,如果 Go 变得像 Java 一样主流,,那么它一定会添加越来越多的特性,,到时候就不简单了
    比如争论了好几年的泛型,,现在不是马上要加了吗?至于异常处理,谁又敢说以后肯定不会加?
    ntgeralt
        112
    ntgeralt  
       2020-10-04 15:54:21 +08:00
    其实我想问,github win 版用什么编译出来,感觉运行很流畅啊
    reus
        113
    reus  
       2020-10-04 16:21:57 +08:00
    @Jirajine rust 不能解决空指针的问题,解一个空指针,和对 None 用 unwrap 是同样性质的事情。既然你认为 unwrap 不是语言的问题,怎么到 go 里解空指针,就成了语言的问题?另外,go 里空指针一样可以调用方法,只要没有解指针的操作,那就是没问题。

    我不想和你谈 checked exception,别的地方已经有很多讨论,也远远说不上“毫无疑问”: https://www.zhihu.com/question/60240474
    王垠很多观点都是错的。
    cmdOptionKana
        114
    cmdOptionKana  
       2020-10-04 16:55:55 +08:00 via Android
    @ntgeralt 和 vscode 一样,基于 Electron 。
    angryfish
        115
    angryfish  
       2020-10-04 18:23:48 +08:00 via iPhone
    先不学了,包管理器已经出来了。我再等等泛型。然后再等等异常处理。估计,就差不多了
    Jirajine
        116
    Jirajine  
       2020-10-04 18:37:42 +08:00
    @reus Option<T>当然能解决空指针的问题,unwrap()是用户主动的,而 go 里指针全部可空,相当于在所有解引用操作时自动调用 unwrap(),当然是语言的问题。
    rust 的类型系统就可以在编译时就确保你必须显式处理 None 的情况,无论是用 pattern match,组合器,还是调用 unwrap()告诉编译器我保证此值不空。不然你根本拿不出来里面的值。
    两者最大的区别是,当你认为一个指针非空而对它进行解引用,它可能是空从而让你 nullpointerexception.
    而 rust 只要你拿到 Option<T>里面的值,就可以确保它一定非空。如果你忘了处理,压根不会通过编译。
    kotlin 里面也有区分 nullability 的类型系统,而 rust 直接通过 enum 就达到了同样的效果。

    至于 checked exception,我上面说的是,它的好处是毫无疑问的,而它的问题(如导致接口无法向下兼容)使用 error type 也可以避免。
    reus
        117
    reus  
       2020-10-04 19:43:01 +08:00
    @Jirajine 是吗?
    这是 github 搜索 unwrap panic language:Rust 的结果: https://github.com/search?l=&q=unwrap+panic+language%3ARust&type=issues
    上万个相关 issue,你确定 rust 可以阻止开发者不当使用 unwrap ?
    no1xsyzy
        118
    no1xsyzy  
       2020-10-04 20:15:48 +08:00
    @reus #113 没多少时间,看前两个回答其实也很半桶水。
    一个是 Java 的 Function 接口定义没写完善的问题,只支持了输入侧的逆变没支持输出侧的协变,因为没有考虑到 throws 也是毫无疑问的返回值的一种,还是 Either 的一种不完整表达所致。
    第二个也是实践上的错误,Exception 分类应当是嵌套封装而不是继承封装,否则永远只能支持协变(至于双变在语言层面上就不支持),对错误处理即调用方不友好,只是目前来说这个错误从 Java 开始一直持续到现在…… 封装一个 Error Code 进去就是正确途径的一种拟似。
    话说说到底,错误处理把逆变和协变关系给调转了是问题所在,至于再深究下去,根本问题是范畴论与变量的变属性兼容比较诡异。

    我的建议是把一些错误处理改换成 Algebraic effects
    no1xsyzy
        119
    no1xsyzy  
       2020-10-04 20:20:54 +08:00
    @no1xsyzy 没写完发出来了,我的建议是把一些错误处理改换成 Algebraic effects,一些错误处理以返回值论。
    这样就可以通过注入一个 handler 来手动调整全局的 EOF 行为。
    Jirajine
        120
    Jirajine  
       2020-10-04 20:23:50 +08:00
    @reus #117 搜 nil pointer language:go 还有 4 万个 issues 呢,这样比较没多大意义。

    这玩意就像防火墙,谁都不能阻止你手动关了它作死,但比起完全没有保护措施你不能说它没用。
    Nugine0
        121
    Nugine0  
       2020-10-04 21:04:55 +08:00 via Android   ❤️ 4
    @reus
    前面人给的 rust 代码是错的,你却拿这个举例,算不算被钓鱼?
    我在谈事实,你却直接攻击别人“双标”。看来这就是某些人的素质,真是“高”啊。

    1. rust 的真正写法是 read(&mut buf)?,或者用两个分支的 match 分别处理成功和失败。成功分支中有可能是已读完的情况。
    go 的 if 是 fall though 的,需要理解的代码范围更多。
    你以为我要说 rust 更优雅?错了。
    我要说的一直是:go 自称简单,但实际上并不简单。
    一个自称简单的语言和一个公认难学的语言比心智负担,说出来丢不丢人?

    2. 我定义的正常函数就是要么成功要么失败,语义不同就是增加负担,对人不友好就是反人类,还要怎么解释?
    io 本来就有固有的复杂度,却在这里节省一次调用,简直莫名其妙。
    我要不要再去研究一下“节省”和“不节省”的等价性?我认为这就是设计失误。你开心就好。

    3. rust 的 lint 的确是语言机制的一部分,具体表现为 rustc 内置 lint 和 clippy 扩展 lint 。我还说 doc 和 tests 也是 rust 语言机制的一部分,请问有没有意见?

    再强调一下我的论点:go 自称简单,但实际上并不简单。

    能看成捧一踩一的,你开心就好。
    reus
        122
    reus  
       2020-10-04 21:23:31 +08:00
    @Nugine0
    错的?他只不过将需要处理的情况都写成 match 分支,到你嘴里就成了错的?
    read(&mut buf)? 根本没处理 Interrupted 。
    用两个 match 分支处理,那分支里面的代码被你吃了? Ok 里面需要处理 0 和非零的情况,Err 里面又有需要特别处理 Interrupted 的情况。
    我可没说过 go 简单,谁和你说 go 简单,你就和他去辩论,别找我,别自己竖个靶子就来打。
    事实?你的观点充满了偏见,拿自己的定义当普遍真理,和你讨论,纯粹就是 flamewar,毫无价值可言。
    Nugine0
        123
    Nugine0  
       2020-10-04 21:31:49 +08:00 via Android
    @reus 再换成 C,没人会说 C 难吧。
    看看 C 有什么,基于错误码的错误处理,预处理器。
    C 也要挨个判断返回值,但人家有宏。
    go 稍微给点语法糖都不会被喷 if err != nil,但还是坚持,估计以后也会一直被喷。

    Linux 内核是用 C 写的,是个人都能轻松学会 C,能读 C 代码不代表能懂。
    go 所谓的简单,真的能让人“懂”大型项目?光阅读有什么用?我看未必,把代码复杂度和语法复杂度混淆起来而已。

    go module 就凭空增加了一项学习成本,将要出来的泛型也是。

    减掉的复杂度最终都会加回来,成为内卷的工具,等着瞧。
    hefish
        124
    hefish  
       2020-10-04 21:35:01 +08:00
    go 的面向对象有点不适应。反而是 php 的面向对象语法居然挺适应。。。ε=(´ο`*)))唉。。。
    Nugine0
        125
    Nugine0  
       2020-10-04 21:39:16 +08:00 via Android
    @reus
    编译不过就是错,不知道你口中的“错”还能是什么?
    你认为我有偏见,你这说法本身就是偏见。搁这套娃呢,火气真大。

    我讲了设计失误,你上来就是“双标”,谁有偏见?
    reus
        126
    reus  
       2020-10-04 21:40:51 +08:00
    @Nugine0 我操,我在这个帖子里讨论了半天的错误处理,是你自己忽然扯出来什么“go 自称简单,但实际上并不简单。”。我有和谁讨论过简单不简单的问题?????????
    你能不能不要自说自话?
    语法糖个屁啊,我就是用一个函数替代 if err != nil 的。喷的都是不会的,就知道人云亦云。
    有学习成本怎么了?又不是弱智,学不会还是怎么了?
    你和 go 有什么深仇大恨你自己解决,你不同意楼主的说法你就直接发帖,别 @ 我,我不和你讨论这些。
    reus
        127
    reus  
       2020-10-04 21:55:10 +08:00
    @Nugine0 编译不过??示意代码还要管能不能编译??

    要看能编译的是吧?

    https://github.com/cpjreynolds/rivet/blob/de560ca3f6cc3f57832e62da811f1c3fd38cc495/src/io.rs#L13

    https://github.com/jonhoo/volley/blob/d115aaba24cce1c939d79594bc4f9ff5c5299fa8/servers/rust/main.rs#L55

    https://github.com/isamert/scheme.rs/blob/acc8dca48c87abeaeb3e75d8790cfd516fb5a6f9/src/utils/chars.rs#L30

    随便都能搜出现成的用例,你要不要去告诉他们,他们这样写是错的?真正的写法是 read(&mut buf)? ?或者 match 用两个分支才是对的,用四五个就是不对?
    Nugine0
        128
    Nugine0  
       2020-10-04 21:59:09 +08:00 via Android
    @reus 写函数替代 if err != nil 不能改变控制流,用 panic/recover 又与你之前的言论矛盾,我很好奇你是怎么写的。

    “就算返回的是指针,且函数有问题,那后面如果遇到解指针,那就会直接 panic,表明程序需要改,是不需要写代码做检查的。”
    CosimoZi
        129
    CosimoZi  
       2020-10-04 22:00:26 +08:00 via Android
    求求你们学点 plt 吧
    Nugine0
        130
    Nugine0  
       2020-10-04 22:02:16 +08:00 via Android
    @reus 绝了,我说“编译不过就是错”,没说必须要用两个分支,真能脑补。
    我也没说 rust 更优雅,我说的是 go io.Reader 是没有 union type 造成的设计失误。
    Nugine0
        131
    Nugine0  
       2020-10-04 22:14:09 +08:00 via Android
    @reus 火气这么大,不如去写点代码冷静一下,然后再来教别人怎么错误处理。
    reus
        132
    reus  
       2020-10-04 22:39:39 +08:00
    @Nugine0 就是用 panic / recover 啊,矛盾在哪里??你对我那句话的理解,明显有严重偏差。

    以标准库的 errors.As 为例,这个函数只返回一个 bool,对于误用的情况,全部直接 panic: https://github.com/golang/go/blob/9706f510a5e2754595d716bd64be8375997311fb/src/errors/wrap.go#L77

    我那句话的意思是,有问题的程序,会直接 panic,而不需要写代码掩盖代码本身的问题。errors.As 也没有返回 error,就是基于这样的设计理念。

    这和用 panic / recover 处理错误,根本毫无关系。

    用 panic / recover 处理错误的方式,go 标准库里面就有: https://github.com/golang/go/blob/869c02ce1f635960bfc2f06bb52e2b4e17eaa199/src/encoding/gob/error.go#L17
    当然,我用的比这个复杂一些,例如 if err != nil,是放到 error_ 里面的。

    我就想问,你们这些喷 if err != nil 的人,知道这个事实吗?知道 go 可以不用 if err != nil 处理错误吗??

    “前面人给的 rust 代码是错的”
    “rust 的真正写法是 read(&mut buf)?,或者用两个分支的 match 分别处理成功和失败。”
    别改口,v2ex 是不能修改历史发言的,你说过什么,想表达什么意思,清清楚楚。

    对着你这种明明水平不高,还满嘴“正常人思维”、“反人类”、“心智负担”、“大道至简”的人,能不火起的人,我会非常佩服他。
    Nugine0
        133
    Nugine0  
       2020-10-04 23:09:52 +08:00 via Android
    @reus

    你对我那句话的理解,明显有严重偏差。

    “错”是指编译不过,“真正”的写法是指能编译通过的写法。非得说成这样才明白?

    你说过什么,想表达什么意思,清清楚楚。

    你说 panic 是代码本身的问题。

    但 error 显然是运行时的问题,二者不能混为一谈。这不是我一个人的看法,网上文章多了去了。

    能用 panic/recover,不代表 if err != nil 就是好的。别人说事物 A 不好,你说可以用事物 B,并反过来指责别人,真是荒谬。

    我倒是很佩服能莫名其妙发火的人,别误会,说的不是你。
    Nugine0
        134
    Nugine0  
       2020-10-04 23:23:53 +08:00 via Android
    @reus 用 panic/recover 来处理 error 就是一种混淆的做法,不然为什么不设计成 try/catch ?

    当然,你完全可以不理会我的看法,毕竟我“水平不高”。
    reus
        135
    reus  
       2020-10-04 23:25:57 +08:00
    @Nugine0 得了吧,你就是那种明明做了,却非要说不是,非要不认的人。
    傻子都知道你想说的是谁,你非要此地无银,“别误会,说的不是你”,真是笑死个人。
    就是不肯承认自己说错了,非要诸多辩解。
    你在其他帖子里怎样吹 ? 运算符,以为没有记录吗?忽然发现 ? 不能包打一切了,忽然发现别人的多个分支的 match 也是对的,就来死嘴硬,说什么编译过了才是“真正的写法”。
    谁不明白你那点心思啊?
    我又什么时候说过 if err != nil 好了啊?你能不能把你这毛病改改,别给别人加戏啊?
    “go 稍微给点语法糖都不会被喷 if err != nil,但还是坚持”,我这不是告诉你用函数就可以替代 if err != nil 吗?倒成了我的不是了?
    reus
        136
    reus  
       2020-10-04 23:32:53 +08:00
    @Nugine0 你这个问题,就等于“豆腐花放糖就是一种混淆的做法,为什么不放盐?为什么不放卤水?”

    你没发现你提出的,都是一些甜咸问题吗?

    甜的就是反人类,咸的就是不反人类,诸如此类。

    我当然不想理会你这种无聊的问题。
    Nugine0
        137
    Nugine0  
       2020-10-04 23:52:39 +08:00 via Android
    @reus 语法糖和函数一样?不然为什么有 check 语法的提案?

    学到个教训,不说清楚就会引来死缠烂打。我原来的意思就是编译错误,因为 io Error 和 io ErrorKind 不同。

    前提都是错的,那什么结论都有可能。你非要往其他方向理解,那你说的都对。
    Nugine0
        138
    Nugine0  
       2020-10-04 23:58:55 +08:00 via Android
    @reus

    有问题的程序,会直接 panic,而不需要写代码掩盖代码本身的问题。

    用 panic / recover 处理错误,正是掩盖了代码本身的问题。

    这不是双标,那什么是双标?
    blless
        139
    blless  
       2020-10-05 03:21:09 +08:00 via Android   ❤️ 1
    叹为观止…原来讨厌 go 的程序员过不去的坎居然还是 if err,有这功夫我 ide 一套快捷键都搞完了
    cmdOptionKana
        140
    cmdOptionKana  
       2020-10-05 08:27:29 +08:00
    @blless 而且 goland 还可以简化显示 if err,连阅读代码的问题都没有。
    pursuer
        141
    pursuer  
       2020-10-05 09:06:37 +08:00 via Android
    @blless 不使用 go 的原因其实也有其他的,比如我需要热重载(动态加载),go 的支持就不太行,最多只能用 plugin,还只有 linux 能用,像 java,c#,c/c++都有动态加载方案,java 和 c#甚至还能共用符号空间,c/c++其实也有一些第三方 elf 加载器可以管理符号空间。然后就是像 c#,kotlin,java 也有不少语法糖和类型推断能力,go 相比较没有什么优势了。不过语言优劣争来争去确实意义不大,符合需求就好。
    reus
        142
    reus  
       2020-10-05 09:34:41 +08:00
    @Nugine0 最开始的四个分支的 match,就是用来说明 rust 可以怎样处理各种情况的,你说它错,又提出用 ? 才是“真正写法”,或者用两个 match 分支,你是对原先多分支 match 的完全否定,甚至说我“被钓鱼”。然而我已经找出了实际的用例给你看了,别人并不是用 ? 运算符,也不是只用两个分支的 match 来处理,完完全全就是像最开始另一位网友给出的示意代码那样,匹配 Ok 的两种情况和 Err 的两种情况。

    你要是不记得自己说过什么,我给你复制出来:
    “前面人给的 rust 代码是错的,你却拿这个举例,算不算被钓鱼?”
    “ rust 的真正写法是 read(&mut buf)?,或者用两个分支的 match 分别处理成功和失败。成功分支中有可能是已读完的情况。”

    你要是非认为前面人给的 rust 代码是错的,那没影响,我可以改用前面 github 上的三个现成的实例。
    麻烦拿你“真正的写法”,或者“两个分支的 match”,去给这些项目提 pull request 。别人接受了,你这段话,才是对的。

    我看你根本就是不懂 go 的,又说你好奇我怎样用 panic / recover 处理错误,结果我给出代码了,你对于关键的问题,完全没了解。
    https://github.com/golang/go/blob/869c02ce1f635960bfc2f06bb52e2b4e17eaa199/src/encoding/gob/error.go#L36
    这一行是关键,用 panic / recover 处理错误,只会 recover 特定类型的错误,并不会 recover 所有 panic 。
    所以,如果有其他方式的 panic,那是会再次抛出的,根本不会被转化成 error 。
    panic / recover 处理错误,处理的是预期中的错误,也就是不用 panic / recover 也要处理的那一类错误
    而非预期的错误,因为不可能是预定义的类型,所以根本就不管,仍然是继续 panic,不会被 recover
    你还不是水平低下?这么简单的事实,你自己理解错误,反而说我双标?
    Hanggi
        143
    Hanggi  
       2020-10-05 10:07:40 +08:00
    Go 有类似 Spark 或 Flink 这种分布式系统吗?
    Nugine0
        144
    Nugine0  
       2020-10-05 10:16:39 +08:00 via Android
    @reus
    我看你是根本不懂 rust 。

    read 的返回值是 Result<usize, io::Error>,不能用 ErrorKind 直接匹配,这是我说“错”的原因。

    两分支是最原本的写法,用来论证“要么成功要么失败”,进一步论证 go 双元组的设计不好。

    你说的四分支是等价版本,把直接跳转改成了 fall through,本质上是按情况处理 union type 的两个可能模式。

    我说 14+29=42 不对,21+21=42 是对的。你偏要说 10+10+10+12=42 也是对的,反过来喷我,我也是服了。

    变点写法就看不懂了?某一层封装一下,不就能用问号运算符了?这么简单的事实,还用我专门说?

    标准库用问号运算符,把 Interrupt 暴露出去,一搜一大把,你难道也要说不对不对别人不是这么写的?

    https://doc.rust-lang.org/src/std/io/buffered.rs.html#274

    回到 go 上

    https://dave.cheney.net/tag/panic

    "When you panic in Go, you’re freaking out, it’s not someone elses problem, it’s game over man."

    把 panic 当 exception 用,就是违反语言机制设计意图的,就是一种滥用。

    再怎么 panic/recover,作者也不敢越过库的边界,无法代替 err 。

    我说这是混淆的写法,有何问题?
    wangxiaoaer
        145
    wangxiaoaer  
       2020-10-05 10:19:09 +08:00 via iPhone   ❤️ 3
    这个假期原本是那么的无聊和枯燥,这个帖子的出现让它不再单调。
    Nugine0
        146
    Nugine0  
       2020-10-05 10:34:42 +08:00 via Android
    @reus

    https://eli.thegreenplace.net/2018/on-the-uses-and-misuses-of-panics-in-go/

    https://golang.org/doc/effective_go.html#recover

    标准库为了简化错误处理去用 panic,违背设计意图,恰恰说明了 err 的繁琐,if err != nil 连标准库的人也不想写。

    能用不代表它就是好的。
    plko345
        147
    plko345  
       2020-10-05 10:51:29 +08:00 via Android
    @reus rust 能解决空指针
    reus
        148
    reus  
       2020-10-05 11:06:37 +08:00
    @Nugine0 你想论证的“要么成功要么失败”,是假命题。因为实际情况有四种或者以上,不是只有两种,你的论证不成立。
    请不要试图篡改事实,事实是别人说了 10+10+10+12 = 42,是你自己跳出来说 21 + 21 = 42 才是“真正写法”,是你自己首先否定 10+10+10+12 = 42,而我没有否定任何一种写法。
    不要再给我加戏,再说一次。

    Dave Cheney 是语言设计者吗?他自己的观点,就能当成设计意图?

    看看 go 官方博客里怎么说的吧: https://blog.golang.org/defer-panic-and-recover

    “For a real-world example of panic and recover, see the json package from the Go standard library. It encodes an interface with a set of recursive functions. If an error occurs when traversing the value, panic is called to unwind the stack to the top-level function call, which recovers from the panic and returns an appropriate error value (see the 'error' and 'marshal' methods of the encodeState type in encode.go).”

    滥用?官方博客直接告诉你可以这样用了。

    不越过库边界和这有什么关系??说的是替代 if err != nil 这种写法,而不是替代 error 。
    承认 go 开发者可以不用写 if err != nil,有那么难吗?
    更不用说很多人根本就不觉得写 if err != nil 有什么问题,就你这种根本就不用 go 的人在瞎逼逼
    dyume
        149
    dyume  
       2020-10-05 11:10:48 +08:00
    java 只学了一点皮毛,可以直接学 go 吗?
    reus
        150
    reus  
       2020-10-05 11:24:15 +08:00
    @Nugine0 这就很搞笑了,你帮我贴出了一篇支持我的观点的文章
    你贴出的 effective go 里明确写了:
    “With our recovery pattern in place, the do function (and anything it calls) can get out of any bad situation cleanly by calling panic. We can use that idea to simplify error handling in complex software. ”
    不用 panic / recover 处理错误,你就在那里喷 if err != nil 繁琐
    用了 panic / recover 处理错误,你就在那里喷违背设计意图
    你就没想过 panic / recover 本来就包含了这个意图?
    怎么你都能喷
    你就是想喷而已

    看看你转移了多少次话题吧:
    “go 稍微给点语法糖都不会被喷 if err != nil,但还是坚持,估计以后也会一直被喷。”
    告诉你可以用函数替代了
    “写函数替代 if err != nil 不能改变控制流,用 panic/recover 又与你之前的言论矛盾,我很好奇你是怎么写的。”
    告诉你怎么写了
    “用 panic/recover 来处理 error 就是一种混淆的做法,不然为什么不设计成 try/catch ?”
    告诉你官方也推荐这么用了
    “能用不代表它就是好的。”
    呵呵,连万能句式都出来了
    reus
        151
    reus  
       2020-10-05 11:32:12 +08:00
    @plko345 把 expect 、unwrap 这些可能 panic 的方法都删除吧
    ztxcccc
        152
    ztxcccc  
       2020-10-05 11:33:43 +08:00
    @blless 你要简单为啥不用 PHP
    ConradG
        153
    ConradG  
       2020-10-05 11:52:53 +08:00
    核心问题其实就一条,golang 不是作为通用高级语言而设计出来的,但是很多人却非要它来做通用高级语言做的事。
    lvdb
        154
    lvdb  
       2020-10-05 11:54:27 +08:00 via Android
    啊?还有不支持泛型的语言??
    blless
        155
    blless  
       2020-10-05 12:00:41 +08:00 via Android
    @ztxcccc 首先,我说的简单不止是语法简单,是语言表达足够简单,php 符号我就不说了各种?$->还有一堆语法你觉得比 go 简单我没话说。
    另外,动态语言跟静态编译语言完全两码事,动态语言不能编译时发现很多代码问题在软件工程上就已经很致命了,再加上动态运行时的性能问题。大型软件用动态语言我只能表示服气好吧?你没看上面都是讨论 rust java 啥的,你 php 边上凉快看戏不爽吗
    Nugine0
        156
    Nugine0  
       2020-10-05 12:06:47 +08:00 via Android
    @reus

    四种或以上的情况难道不能归类为 成功 和 失败?别人贴的模式匹配是错的,难道不能指出来?写法是等价的,你看不出来,好意思说我?也奉劝你,不要再给我加戏。

    语法糖不等于函数,明明是你在转移话题。

    上网查查,panic 在编程语言中的语义就是恐慌,是致命的。我也明确说了,把 panic 当 exception 就是滥用? Dave 确实不能代表设计意图,我也不能断定,难道你就能代表?懂的都懂,比你我更懂,行了吧?

    我看是没有必要再回了,继续下去没有任何意义。
    reus
        157
    reus  
       2020-10-05 12:34:14 +08:00   ❤️ 1
    @Nugine0 那我就继续更新呗:
    “go 稍微给点语法糖都不会被喷 if err != nil,但还是坚持,估计以后也会一直被喷。”
    告诉你可以用函数替代了
    “写函数替代 if err != nil 不能改变控制流,用 panic/recover 又与你之前的言论矛盾,我很好奇你是怎么写的。”
    告诉你怎么写了
    “用 panic/recover 来处理 error 就是一种混淆的做法,不然为什么不设计成 try/catch ?”
    告诉你官方也推荐这么用了
    “能用不代表它就是好的。”
    呵呵,连万能句式都出来了
    “语法糖不等于函数”
    明明你不想写 if err != nil,那现有的机制就能解决,怎么变成非语法糖不可了?
    这就是明显的转移话题,试图把话题从“if err != nil”,转移到“语法糖”

    用 panic / recover 可不是我提出来的,是官方标准库,官方博客,官方 FAQ 里都有的。
    别试图偷换概念。
    fpure
        158
    fpure  
       2020-10-05 12:37:01 +08:00 via Android   ❤️ 4
    又见识了一场语言圣战
    sharpy
        159
    sharpy  
       2020-10-05 15:14:23 +08:00
    Result Option 这些结构只要支持代数数据类型都能自己写吧,这有啥可争的?
    Nugine0
        160
    Nugine0  
       2020-10-05 17:35:19 +08:00 via Android
    @reus 看来你还没懂为什么现有机制不好用,不够用,不合适用。

    if err != nil 的处理通常是提前返回,处理起来非常重复繁琐。但函数不能改变控制流,panic 或类似 try 和 check 的新语法才能改。

    panic/recover 是用来处理致命错误的,这是共识。拿它来代替普通错误处理是不合适的,这也是常见观点。跨越库的边界时,还得老老实实地判断错误。

    真合适的话,官方为什么不设计成 try/catch ?真够用的话,那些官方参与的错误处理提案和讨论难道在放屁?

    以前还有 go 不需要泛型的说法,现在呢?
    neoblackcap
        161
    neoblackcap  
       2020-10-05 17:36:26 +08:00
    @sharpy 你自己实现的,人家可不一定会用。这是语言生态的的争论。社区不会一定会用非标准库的东西。
    reus
        162
    reus  
       2020-10-05 19:34:35 +08:00
    @Nugine0 “跨越库的边界时,还得老老实实地判断错误”,大错特错。
    说了半天,你都不知道别人怎么用的。
    例如使用标准库来写一个复制文件内容的函数,可以这样写:
    func CopyFile(path string, w io.Writer) (err error) {
    defer catch(&err)
    f, err := os.Open(path)
    check(err)
    defer f.Close()
    _, err := io.Copy(w, f)
    check(err)
    return nil
    }
    有“if err != nil”吗?没有。就是这么简单的一件事情。

    这种处理错误的方式,本来就没有说错误不是用 error 返回,也不会将抛出 panic 作为接口的一部分。
    你扯什么 try/catch ?你该不会以为说用 panic/recover 处理错误,就等于不要 error 返回值吧?
    我看你根本对于“现有机制”没有足够的了解。

    阻止你使用这种方式来处理错误的,只是你的偏见,而不是什么共识。
    你根本不用管别的库怎么处理错误,因为大家都是用 error 返回的。
    在你的代码里,你喜欢写 if err != nil 就写,不喜欢写就可以用上面的方式,根本不涉及跨库。

    “go 稍微给点语法糖都不会被喷 if err != nil,但还是坚持,估计以后也会一直被喷。”
    喷子永远有喷的理由,一个理由站不住脚了,就找另一个。
    你看喷包管理的不见了,喷没有泛型的也不见了。
    当然,也有角度清奇的,“内卷工具”,佩服,这都想得出。

    “以前还有 go 不需要泛型的说法”,这也是由来已久的偏见和误解,官方的态度从来都不是不需要,而是没发现好的实现方式。直到现在有了泛型的提案,对于具体的实现方式,也还没有确定,还在讨论: https://groups.google.com/g/golang-dev/c/OcW0ATRS4oM/m/5-kMv9i-BQAJ
    monkeyWie
        163
    monkeyWie  
       2020-10-05 19:39:48 +08:00
    go 确实好用,但是错误处理是缺点这个就别洗了吧
    Nugine0
        164
    Nugine0  
       2020-10-05 21:44:14 +08:00 via Android   ❤️ 1
    @reus
    把 if err != nil 换成 check(err),还是填一发打一枪。写个函数就不是“判断错误”了?真搞笑。

    你敢把 panic 作为普通错误,作为接口的一部分,抛给库的使用者吗?如果不敢,就说明 panic/recover 不是错误处理的正解,如果敢,那祝你好运,别被 issue 挂起来。

    如果未来喷 if err != nil 的不见了,那真是一件好事,这说明 go 终于拿出了好用的错误处理方案。

    我说 go 的现有机制不行,需要新语法解决。你不肯接受,我还能咋办,难道顺着网线去 git push -f ?
    我不会再浪费时间了,到此为止。
    reus
        165
    reus  
       2020-10-05 22:34:19 +08:00
    @Nugine0 继续更新你转移话题的历史:
    “go 稍微给点语法糖都不会被喷 if err != nil,但还是坚持,估计以后也会一直被喷。”
    告诉你可以用函数替代了
    “写函数替代 if err != nil 不能改变控制流,用 panic/recover 又与你之前的言论矛盾,我很好奇你是怎么写的。”
    告诉你怎么写了
    “用 panic/recover 来处理 error 就是一种混淆的做法,不然为什么不设计成 try/catch ?”
    告诉你官方也推荐这么用了
    “能用不代表它就是好的。”
    呵呵,连万能句式都出来了
    “语法糖不等于函数”
    明明你不想写 if err != nil,那现有的机制就能解决,怎么变成非语法糖不可了?
    这就是明显的转移话题,试图把话题从“if err != nil”,转移到“语法糖”
    “把 if err != nil 换成 check(err),还是填一发打一枪。写个函数就不是“判断错误”了?真搞笑。”
    函数写给你了,怎么改变控制流也告诉你了,这里的 panic 不会跨越包边界也告诉你了,现在忽然又转移话题,用函数判断错误就不行了??

    看看你前面的论点,还有多少站得住脚吧。
    也难怪你不停转移话题,不就是想让人忘记你前面说过的话嘛,让人忘记你的错误嘛。
    所以我就要复制出来,免得跑题了。

    我写的 ReadFile,很明显就有一个 error 返回值,我说用 panic/recover 做错误处理,从来就没有说要抛出 panic 给调用者,你以为用 panic/recover 处理错误,就等于抛 panic 给调用者,这是你的错误理解。
    你以为是函数实现者用 panic 抛出,调用者用 recover 接住?
    不是的,panic/recover,都是函数实现者一侧调用的,上面的 check 里用了 panic,catch 里用了 recover,这一对是在同一个函数里用的,recover 是用来将 panic 抛出的错误,赋值给 error 返回值用的。
    根本不是你理解的那样,将 panic 抛给调用者。

    这都还不明白的话,那我也确实没法再说什么,你水平太低。
    leavic
        166
    leavic  
       2020-10-05 22:37:09 +08:00
    我还是第一次看到有一种语言是这样逼迫别人赶紧学它的:你丫赶紧的,明年你就学不会我了。
    ericgui
        167
    ericgui  
       2020-10-06 00:43:27 +08:00
    @leavic +1
    beidounanxizi
        168
    beidounanxizi  
       2020-10-06 01:46:57 +08:00
    go 还是值得学习 至于你说的泛型带来的优势 不见得
    我不喜欢 go 的泛型,因为 JAVA 已经有了
    go 比较讨喜的就是多返回值和 go 的函数第一公民了
    看到 spring 框架满屏的 aop 贼恶心
    beibeijia
        169
    beibeijia  
       2020-10-06 02:34:24 +08:00
    把 "if err != nil" 改成 "$" 就完美了(手动 doge
    273579540
        170
    273579540  
       2020-10-06 07:59:24 +08:00
    www.learnfk.com/course-go 推荐一个页面教程
    wwwicbd
        171
    wwwicbd  
       2020-10-06 23:57:09 +08:00
    刚开始练习 Golang, 写 Web APP 用官方的 http 包就基本足够了.
    自己抽取了一点常用功能做了几个包, 请各位批评指点:

    1. RESTFul 的路由
    https://github.com/icbd/go_restful_routes

    支持路径直接匹配, 路径前缀匹配, 路径参数匹配和捕获, 正则匹配. 还有静态文件的路由重定向.

    2. 默认值填充
    https://github.com/icbd/default_box

    Golang 的结构体会自动填充零值, 但是很多时候我们想让他填充非零值的默认值. 利用了反射和 tag 实现了默认值的填充.
    necodba
        172
    necodba  
       2020-10-07 11:33:50 +08:00
    好了好了都是大佬吵架,喜闻乐见的语言之争,我就说一句,PHP 才是最好的语言
    mamahaha
        173
    mamahaha  
       2020-10-08 11:40:09 +08:00
    想给人打工就都得学,想自己玩可以根据项目需求去选择。
    AlexSLQ
        174
    AlexSLQ  
       2020-10-15 14:36:41 +08:00
    quxiangxuanqxx
        175
    quxiangxuanqxx  
       2020-11-15 11:32:22 +08:00
    大佬,我有个疑问,你说 echo 框架层面实现了每个请求是一个 goroutine

    但是我看了下代码,框架层面并没有用 goroutine

    应该是 net/http 实现的是每个请求是一个 goroutine 吧

    ref:
    https://github.com/golang/go/blob/master/src/net/http/server.go#L3304
    kidlj
        176
    kidlj  
    OP
       2020-11-15 12:30:58 +08:00 via iPhone   ❤️ 1
    @quxiangxuanqxx 嗯,你说得对,我说错了。
    b00tyhunt3r
        177
    b00tyhunt3r  
       2020-11-17 09:42:28 +08:00 via iPhone
    谢谢大熊弟,想问一下
    “Go 的并发很简单,只有 Goroutine 和 channel 一种方式。官方出的那本书里讲解得非常清晰,必读。”
    那本书是指?
    kidlj
        178
    kidlj  
    OP
       2020-11-17 10:04:32 +08:00
    @b00tyhunt3r <The Go Programming Language> Go 程序设计语言。
    b00tyhunt3r
        179
    b00tyhunt3r  
       2020-11-17 10:15:33 +08:00
    @kidlj 🙏
    jmyz0455
        180
    jmyz0455  
       2020-11-26 10:39:41 +08:00
    我还没接触过 Go,有个疑问,就是 Go 现在还没有写 Web 的全家桶吗?开箱即用的 CURD 库也少?
    1  2  
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1769 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 36ms · UTC 00:41 · PVG 08:41 · LAX 17:41 · JFK 20:41
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.