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

Go 语言真的有这么破烂不堪吗

  •  1
     
  •   jlak · 2024-08-14 17:25:41 +08:00 via iPhone · 18454 次点击
    这是一个创建于 392 天前的主题,其中的信息可能已经有所发展或是发生改变。
    前言:
    第一次认识 Go 在十几年前了,当时玩着 Python
    从那时候印象(没看过代码)里就一直非常好
    感觉速度又快又简单
    直到最近才开始上手,体验是简单到超乎我的意料
    然后感觉深刻的错误判断非常非常的繁琐
    几乎每个函数里需要写多个 err!=nil
    对于我这种只会写写简单代码的 err!nil 有时超过业务逻辑
    但这好处也很大 几乎将所有错误都归到了可视范围

    正题:
    自从开始正式关注之后,知乎 App 就开始推送大量的 Go 问题的回答(我没有在知乎上关注,应该是根据大数据)
    其中绝大部分都是喷 Go 的
    而且这个量非常的大 每天都会收到多篇
    范围涵盖了 Go 的方方面面
    这个量远远超过了我同样关注的 JS/Node
    一开始不当回事 但是每天这么多推送
    不禁让人重视这个问题
    125 条回复    2024-08-18 00:32:57 +08:00
    1  2  
    yb2313
        101
    yb2313  
       2024-08-15 21:06:03 +08:00
    bang dream it's my go
    Trim21
        102
    Trim21  
       2024-08-15 21:35:19 +08:00
    @yu1miao #100 你把上面那个也改成 var buf io.ByteWriter = &Buffer{} 就看出区别了

    var _ IBuffer = (*Buffer)(nil)
    var _ io.ByteWriter = (*Buffer)(nil)

    func write(w io.ByteWriter) {
    for i := byte(0); i < 10; i++ {
    _ = w.WriteByte(i)
    }
    }

    func writeGeneric[W io.ByteWriter](w W) {
    for i := byte(0); i < 10; i++ {
    _ = w.WriteByte(i)
    }
    }

    func BenchmarkExtract(b *testing.B) {
    var buf io.ByteWriter = &Buffer{}
    for i := 0; i < b.N; i++ {
    write(buf)
    }
    }

    func BenchmarkExtra(b *testing.B) {
    var buf IBuffer = &Buffer{}
    for i := 0; i < b.N; i++ {
    writeGeneric(buf)
    }
    }

    BenchmarkExtract-16 1000000000 4.872 ns/op 0 B/op 0 allocs/op
    BenchmarkExtra-16 1000000000 25.43 ns/op 0 B/op 0 allocs/op
    Trim21
        103
    Trim21  
       2024-08-15 21:48:19 +08:00
    @yu1miao #100 准确的说这里有 6 种情况,你 benchmark 跑一下就能看出来泛型实现的问题了。

    这里跟 writeGeneric 直接传结构体的性能跟用接口,并且编译器没有进行 devirtualize 是相同的,并不会像你说的那样专门生成一个 writeGeneric(w *Buffer) error

    如果你传进去的是一个接口,调用开销直接就翻倍了。


    func Benchmark_interface_0(b *testing.B) {
    var buf = &Buffer{}
    for i := 0; i < b.N; i++ {
    write(buf)
    }
    }

    func Benchmark_interface_1(b *testing.B) {
    var buf io.ByteWriter = &Buffer{}
    for i := 0; i < b.N; i++ {
    write(buf)
    }
    }

    func Benchmark_interface_2(b *testing.B) {
    var buf IBuffer = &Buffer{}
    for i := 0; i < b.N; i++ {
    write(buf)
    }
    }

    func Benchmark_generic_0(b *testing.B) {
    var buf = &Buffer{}
    for i := 0; i < b.N; i++ {
    writeGeneric(buf)
    }
    }

    func Benchmark_generic_1(b *testing.B) {
    var buf io.ByteWriter = &Buffer{}
    for i := 0; i < b.N; i++ {
    writeGeneric(buf)
    }
    }

    func Benchmark_generic_2(b *testing.B) {
    var buf IBuffer = &Buffer{}
    for i := 0; i < b.N; i++ {
    writeGeneric(buf)
    }
    }

    Benchmark_interface_0-16 232742709 5.139 ns/op 0 B/op 0 allocs/op
    Benchmark_interface_1-16 434387566 2.794 ns/op 0 B/op 0 allocs/op
    Benchmark_interface_2-16 85636600 14.67 ns/op 0 B/op 0 allocs/op
    Benchmark_generic_0-16 85636600 13.97 ns/op 0 B/op 0 allocs/op
    Benchmark_generic_1-16 54496902 24.23 ns/op 0 B/op 0 allocs/op
    Benchmark_generic_2-16 49954623 24.91 ns/op 0 B/op 0 allocs/op
    maybeok
        104
    maybeok  
       2024-08-15 21:53:25 +08:00
    我是 PHP 转 go 的,go 让我觉得很麻烦的一点就是每次修改都不能热更新,需要我重新 run 一次,不像 PHP 改完刷新就行了。 我觉得 go 是世界上最好的语言,但 PHP 是宇宙第一的语言,yyds
    hez2010
        105
    hez2010  
       2024-08-15 22:05:12 +08:00
    @james122333 问题就在于错误/异常这种东西恰好不应该通过多返回值来解决,因为错误和正常是互斥的。go 硬是把一个需要用 sum types 表达的东西用 product types 表达了出来。
    正确设计应该返回 Result | Error 作为结果,而不是 Result * Error 。
    yu1miao
        106
    yu1miao  
       2024-08-15 22:09:10 +08:00
    @Trim21 #103 看来确实没那么理想,学到了 :-)
    james122333
        107
    james122333  
       2024-08-15 22:20:54 +08:00
    @hez2010

    重点在於不是所有错误都是不可接受的 照这种情况便要在接近的外层捕获 而单返回值也不能够表示 return nil, nil 这种状况 所以我才提那个例子 可以处理 error 也可以 panic
    james122333
        108
    james122333  
       2024-08-15 22:22:30 +08:00
    @hez2010

    单返回值也只能够一直嵌套结构这种很丑的做法
    Trim21
        109
    Trim21  
       2024-08-15 22:44:48 +08:00
    @james122333 #108

    你说的这几个其实都不是问题。

    单返回值也可以表示 return nil, nil , 只要这个 Result 是 *T | Error 就行。你是想的 int | Error 不能 nil ,nil ,但你定义为 *int | Error 肯定可以 nil ,nil 。nil 也是一个 *int 。

    所以 rust 的解决方法是加了一个 ? 操作符,在 Err 时候直接把 Err 返回上层,中止当前函数。
    ashong
        110
    ashong  
       2024-08-15 22:51:00 +08:00
    Go 起码能看懂, 近几天看 tauri 源码,rust 整个就是天书
    james122333
        111
    james122333  
       2024-08-15 23:31:25 +08:00 via Android
    @Trim21

    这不是白说了吗 我需要同时判断两者
    Trim21
        112
    Trim21  
       2024-08-15 23:52:34 +08:00
    @james122333 #111 除非你在 err!=nil 的时候也返回了一个 T ,否则我们说的 Result 是涵盖了你说的这种情况的。
    jiangzm
        113
    jiangzm  
       2024-08-16 00:42:28 +08:00
    Go 有一部分奇怪的语法只是为了标新立异,同样是 Google 推出的语言 Dart 就正常很多,类 C 的语法很容易让其他语言开发者上手。
    dwu8555
        114
    dwu8555  
       2024-08-16 08:30:26 +08:00
    Hacknews 上最频繁出现的就是 Golang 和 Postgres
    jackblack369
        115
    jackblack369  
       2024-08-16 09:12:12 +08:00
    历经过 javaer 、pythoner ,这月开始入坑的 gopher ,感觉挺好,个人挺喜欢
    james122333
        116
    james122333  
       2024-08-16 09:51:43 +08:00 via Android
    @Trim21

    你这表示指的是"或"的意思吗 如果是不能表示回传两个 nil 如果是结构含两者那我说过了很丑
    必需得要写新结构
    molika
        117
    molika  
       2024-08-16 11:46:21 +08:00
    只能说交叉编译太爽了. 心智负担比 rust 低. 二进制包完爆 py
    写起来习惯就好了.
    xiaocaiji111
        118
    xiaocaiji111  
       2024-08-16 11:56:11 +08:00
    做业务没 java 爽,其他还行,当然也得看是什么业务,电商这些,国内只有字节在搞吧,也能搞,但是不太爽。
    mars2023
        119
    mars2023  
       2024-08-16 11:59:36 +08:00
    @maybeok #104 你说的热更新问题,在静态语言上都是如此吧;鱼与熊掌不可兼得。
    Trim21
        120
    Trim21  
       2024-08-16 12:53:12 +08:00 via Android
    @james122333 这里说的 Result 是提供判断是否为 Error 还是为 result 的能力的,同时判断两者为 nil 就是非 error 并且 *T 为 nil 的情况啊?

    这里说的这个 Result 在 go 里是不存在的,不是结构体那样的东西。结构体和多返回值是一回事。
    james122333
        121
    james122333  
       2024-08-16 17:55:41 +08:00 via Android
    @Trim21

    回传*T 非 nil 为成功 回传 error 为失败 回传 nil 为可接受失败(nil,nil)的意思?
    rust 有这能力? 其它语言这样搞还要判断回传类型 又多了麻烦
    cqu1980
        122
    cqu1980  
       2024-08-16 20:50:14 +08:00
    @uiosun 不是所有的 err 都需要去处理,所以有些直接_略过就行了
    Trim21
        123
    Trim21  
       2024-08-17 09:58:11 +08:00 via Android
    @james122333 有啊
    zizon
        124
    zizon  
       2024-08-17 12:55:18 +08:00
    v2 上也很多喷时政的,想明白了就释然了
    lysShub
        125
    lysShub  
       2024-08-18 00:32:57 +08:00
    @me1onsoda 用数组啊
    1  2  
    关于   ·   帮助文档   ·   自助推广系统   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5692 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 06:26 · PVG 14:26 · LAX 23:26 · JFK 02:26
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.