Go   发布时间:2022-04-09  发布网站:大佬教程  code.js-code.com
大佬教程收集整理的这篇文章主要介绍了驳2B文 "我为什么放弃Go语言"大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。

 

 

 

此篇文章流传甚广,其实里面没啥干货, 而且里面很多观点是有问题的. 这个文章在 golang-china 很早就讨论过了. 最近因为 Rust 1.0 和 1.1 的发布,导致这个文章又出来毒害读者. 所以写了这篇反驳文章,指出其中的问题.

原文链接http://www.voidcn.com/article/p-dhylvfso-bae.html

确实是非常主观的结论,因为里面有不少有问题的观点(用来忽悠Go小白还行).

第0节:我的Go语言经历

在2009年Go刚发布时,确实是因为“Google公司制造”的光环而吸引了(包括文章作者和诸多IT记者)很多低级的尝鲜者. 还好,经过5年的发展,这些纯粹因为光环来的投机者所剩已经不多了(Google趋势). 目前,真正的Go用户早就将Go用于实际的生产了.

说到 其语法中的分号和花括号不满,我想说这只是你的 个人主观感受,还有很多人对Go的分号和花括号很满意,包括水果公司的的 Swift 的语言设计者也很满意这种风格(Swift中的分号和花括号和Go基本相同).

如果只谈 个人主观感受,我也可以说 Rust 的 fn 缩写也很蛋疼!

这个到是事实,在 golang-china 有不少吵架的帖子,感兴趣的可以去挖下,我就不展开说了.

真的不清楚楼主说的可以在 Go1.0 之前短时间内能实现的 重大改进和诸多明显缺陷 是什么.

如果是楼主说前面的 其语法中的分号和花括号不满 之类的重大改进,我只能说这只是你的 个人主观感受 而已,你的很多想法只能说服你自己,没办法说服其他绝大部分人(不要以为像C++或Rust那样什么特性都有就NB了,各种NB特性加到一起只能是 要你命3000,而绝对不会是什么 银弹).

Go 1.1的Release Note,发现语言层面没有太大改变. 语言层没有改变是是因为 Go1 作出的向后兼容的承诺. 对于工业级的语言来说, Go1 这个只能是优点. 如果连语言层在每个版本都会出现诸多大幅改进,那谁还敢用Go语言来做生产开发呢(我承认Rust的改动很大胆,但也说明了Rust还处于比较幼稚和任性的阶段)?

说 Go语言社区里的某些人固执 的观点我是同意的. 但是这些 固执 的人是可以讲道理的,但是他们对很多东西的要求很高(特别是关于Go的设计哲学部分). 只要你给的建议有依据(语言的设计哲学是另外一回事情),他们绝对不会盲目的拒绝(只是讨论的周期会比较长).

关于楼主提交的给Go文件添加BOM的文章,需要补充说明下.

在Go1.0发布的时候,Go语言的源文件(.go)明确要求必须是UTF8编码的,而且是无BOM的UTF8编码的(G公司的Protobuf也不支持带BOM的UTF8编码).

注意: 这个 无BOM的UTF8编码 的限制仅仅是 针对 Go语言的源文件(.go).

这个限制并不是说不允许用户处理带BOM的UTF8的txt文件!

我觉得对于写Go程序来说,这个限制是没有任何问题的,到目前为止,我还从来没有使用过带BOM的.go文件.

不仅是因为带BOM的.go文件没有太多的意义,而且有很多的缺陷.

BOM的原意是用来表示编码是大端还是小端的,主要用于UTF16和UTF32. 对于 UTF8 来说,BOM 没有任何存在的意义(正是Go的2个作者发明了UTF8,彻底解决了全球的编码问题).

但是,在现实中,因为MS的txt记事本,对于中文环境会将txt(甚至是C/C++源文件)当作GBK编码(GBK是个烂编码),为了区别到底是GBK还是UTF8,MS的记事本在前面加了BOM这个垃圾(被GBK占了茅坑),这里的bom已经不是表示字节序本意了. 不知道有没有人用ms的记事本写网页,然后生成一个带bom的utf8网页肯定很有意思. 这是MS的记事本的BUG: 它不支持生成无BOM的UTF8编码的文本文件!

这些是现实存在的带BOM的UTF8编码的文本文件,但是它们肯定都不是Go语言源文件!

所以说,Go语言的源文件即使强制限制了无BOM的UTF8编码要求,也是没有任何问题的(而且我还希望有这个限制).

然后来Go源文件接受带BOM的UTF8了,但是运行 go fmt 之后,还是会删除掉BOM的(因为BOM就是然并卵). 也就是说 带 BOM 的 Go 源文件是不符合 Go语言的编码风格的, go fmt 会强制删除 BOM 头.

前面说了BOM是MS带来的垃圾,但是BOM的UTF8除了然并卵之外还有很多问题,因为BOM在String的开头嵌入了垃圾,导致正则表达式,String的链接运算等操作都被会被BOM这个垃圾所污染. 对于.go语言,即使代码完全一样,有BOM和无BOM会导致文件的MD5之类的校验码不同.

所以,我觉得Go用户不用纠结BOM这个无关紧要的东西(语言源文件不是文本编辑器,没必要支持各种文件格式).

第1节:我为什么对Go语言不爽?

1.1 不允许左花括号另起一行

我觉得Go最伟大的发明是 go fmt,从此Go用户不会再有花括弧的位置这种无聊争论了(当然也少了不少灌水和上tiobe排名的机会). 只给用户一条路,不给任何走歧途的机会, 确保正确、高效。

是这优点,Swift 语言也使用和 Go 类似的风格(当然楼主也可能鄙视swift的作者).

1.2 编译器莫名其妙地给行尾加上分号

又是楼主的 个人主观感受,不过我很喜欢这个特性. Swift 语言也是类似.

1.3 极度强调编译速度,不惜放弃本应提供的功能

编译速度是很重要的,如果编译速度够慢,语言再好也不会有人使用的. 比如C/C++的增量编译/预编译头文件/并发编译都是为了提高编译速度. Rust1.1 也号称 比 1.0 的编译时间减少了32% (注意: 不是运行速度).

当然,Go刚面世的时候,编译速度是其中的一个设计目标.

不过我想楼主,可能想说的是因为编译器自己添加分号而导致的编译错误的问题. 我觉得Go中 { 不能另起一行是语言特性,如果修复这个就是引入了新的错误.

其他的我真想不起来还有哪些 调编译速度,不惜放弃本应提供的功能 (不要提泛型,那是因为还没有好的设计).

最重要是的保持Compiler的靠谱、精简、高效,而不是功能花哨,bug一堆。这样有利于做流水优化、指令集精减、易于跨平台、降低维护负担。

1.4 错误处理机制太原始

话说,软件开发都发展了半个世纪,还是无实质性改进. 不要以为弄一个异常的语法糖就是革命了.

我只能说错误和异常是2个不同的东西,将所有错误当作异常那是SB行为.

try..catch原理是jump/longjump,这种东西会增加底层复杂性,并且容易滥用,不好维护,而且可能会增加10W数量级别goruTine上下文swich负担。

正因为有异常这个所谓的银弹,导致很多等着别人帮忙擦屁股的行为(注意 shit 函数抛出的绝对不会是一种类型的 shit,而被其间接调用的各种 xxx_shit 也可能抛出各种类型的异常,这就导致 catch 失控了):

@H_301_186@int @H_4_131@main() { try { shit(); } catch( /* 到底有几千种 Exception ? */) { ... } } 

Go的建议是 panic - recover 不跨越边界,也就是要求正常的错误要由pkg的处理掉. 这是负责任的行为.

再说Go是面向并发的编程语言,在海量的 goroutIne 中使用 try/catch 是不是有一种不伦不类的感觉呢?

1.5 垃圾回收器(GC)不完善、有重大缺陷

这是说的是32位系统,这绝对不是Go语言的重点应用领域!! 我可以说Go出生就是面向64位系统和多核心cpu环境设计的. (再说 Rust 目前好像还不支持 XP 吧,这可不可以算是影响巨大?)

32位当时是有问题,但是对实际生产影响并不大(请问楼主还是在用32位系统吗,还只安装4GB的内存吗). 如果是8位单片机环境,建议就不要用Go语言了,直接C语言好了.

而且这个问题早就不存在了(大家可以去看Go的发布日志).

Go的出生也就5年时间,GC的完善和改进是一个持续的工作,2015年8月将发布的 Go1.5将采用并行GC,每次 "stop the world" 时间低于 10 毫秒,具体请参 GopherCon2015: Go GC: Solving the Latency Problem in Go 1.5.

关于GC的被人诟病的地方是会导致卡顿,但是我以为这个主要是因为GC的实现还不够完美而导致的. 如果是完美的并发和增量的GC,那应该不会出现大的卡顿问题的.

当然,如果非要实时性,那用C好了(实时并不表示性能高,只是响应时间可控).

对于Rust之类没有GC的语言来说,想很方便的开发并发的@L_674_79@程序那几乎是不可能的.

不要总是吹Rust能代替底层/中层/上层的开发,我们要看有谁用Rust真的做了什么.

1.6 禁止未使用变量和多余import

这个问题我只能说楼主的吐槽真的是没水平.

为何不使用的是错误而不是警告? 这是为了将低级的bug消灭在编译阶段(大家可以想下C/C++的那么多警告有什么卵用).

而且, import 即使没有使用的话,是用副作用的,因为 import 会导致多个init函数全局变量的初始化,导致程序不可控. 如果某些代码没有使用,为何要执行 init 这些初始化呢?

如果是因为调试而添加的变量,那么调试完删除不是很正常的要求吗?

如果是因为调试而要导入fmtlog之类的包,删除调试代码后又导致 import 错误的花,楼主难道不知道在一个独立的文件包装下类似的辅助调试的函数吗?

import (
	@H_404_266@"fmt" @H_404_266@"log" ) func logf(format String,a ...interface{}) { file,line := callerFileLine() fmt.Fprintf(os.Stderr,@H_404_266@"%s:%d: ",file,linE) fmt.Fprintf(os.Stderr,format,a...) } func fatalf(format String,a...) os.Exit(1) } 

import _ 是有明确行为的用法,就是为了执行包中的 init 等函数(可以做某些注册操作).

将警告当作错误是Go的一个哲学,当然在楼主看来这是白痴做法.

1.7 创建对象的方式太多令人纠结

C++的new是狗屎. new导致的问题是构造函数和普通函数的行为不一致,还有加不加(),行为不一致, 这个补丁特性真的没啥优越的.

我还是喜欢C语言的 fopen 和 @H_19_24@malloc 之类构造函数,构造函数就是普通函数,Go语言中也是这样.

C++中,除了构造不兼容普通函数,析构函数也是不兼容普通函数. 这个而引入的坑有很多吧.

1.8 对象没有构造函数和析构函数

defer 可以覆盖析构函数的行为,当然 defer 还有其他的任务. Swift2.0 也引入了一个简化版的 defer 特性.

1.9 defer语句的语义设定不甚合理

前面说到 defer 还有其他的任务,也就是 defer 中执行的 recover 可以捕获 panic 抛出的异常. 还有 defer 可以在 return 之后修改命名的返回值.

上面2个工作要求 defer 只能在函数退出时来执行.

楼主说的 defer 是类似 Swift2.0 中 defer 的行为,但是 Swift2.0 中 defer 是没有前面2个特性的.

Go中的defer是以函数作用域作为触发的条件的,是会导致楼主说的在 for 中执行的错误用法(哪个语言没有坑呢?).

不过 for 中 局部 defer 也是有办法的 (Go中的defer是以函数作用域):

for { @H_301_186@func(){ f,err := os.Open(...) defer f.Close() }() } 

在 for 中做一个闭包函数就可以了. 自己不会用不要怪别人没告诉你.

Swift 的块级 defer 也不方便实现以下的场景:

func (t *T) Serve() {
    if Debug { log.Println(t,@H_404_266@"starTing") defer log.Println(t,@H_404_266@"exiTing") } // stuff } 

Nigel Tao 给的 解释:

The longer answer is that while there@H_404_266@‘s benefit of a scope-scoped defer,there‘s also benefit in a @H_301_186@function-scoped defer. This code: func foo(filename String) error { var r io.Reader if filename != @H_404_266@"" { f,err := os.Open(fileName) if err != nil { return err } defer f.Close() r = f } else { r = Strings.NewReader(fakeInput) } // More code that reads from r. etc } 

1.10 许多语言内置设施不支持用户定义的类型

说到底,这个是因为对泛型支持的不完备导致的. 记得1.5以后可以自定义strct来支持 for,chAnnel,map等。

Go语言是没啥NB的特性,但是Go的特性和工具组合在一起就是好用.

这就是Go语言NB的地方.

1.11 没有泛型支持,常见数据类型接口丑陋

Go有自己的哲学,如果能有和目前哲学不冲突的泛型实现,他们是不会@L_675_136@的.

如果只是简单学学(或者叫抄袭)已经开源的语言的语法,那是C++的设计风格(或者说C++从来都是这样设计的,有什么特性就抄什么),导致了各种脑裂的编程风格.

编译时泛型和运行时泛型可能是无法完全兼容的,看这个例子:

type Adder<T> interface { Add(a,b T) T } 

请问 Adder<int> 和 Adder<float> 是一个接口吗?

type Adder interface { Add(a,b interface{}) interface{} } 

对于这种场景, interface{} 性能不是最好,但是接口却是一致的:

而且,目前已经有 go generate 可以弥补范型和宏部分的不足.

golang-china 关于该文的讨论中有涉及到泛型的讨论.

感觉Go即使真有泛型,也得等到Go2.0了(猜测Go2.0能在2020年诞生10周年发布).

1.12 实现接口不需要明确声明

Go是面向组合的,和UNIX的哲学类似. 使用Go你要知道 io 放的是什么, fmt 包放的是什么,习惯之后会很方便.

你不能说UNIX的命令行工具sort没有实现强的接口依赖检测会有很多问题. 如果你非要乱用sort的捣蛋话当然有很多问题.

但是Go给想组合和合作的人使用的,组合优于继承.

不要提 老赵 那个文章了,我发了反驳文章后他已经闭嘴了: http://my.oschina.net/chai2010/blog/122400

对于IDE环境,Go的工具 go Oracle可以回答某类型实现了哪些接口这类问题.

1.13 省掉小括号却省不掉花括号

代码比较简洁”,谁告诉你是这个原因了? 不懂别瞎说!

必须花括弧的原因是C语言中 if else 的悬挂问题:

if(1) ....; if(2) ...; else ...; 

请问上面的 else 是属于哪个 if 的?

必须加花括弧可以避免上面的问题.

而小括弧又不是必须的因此就去掉了(Swift同样用了Go的设计).

至于 x?a:b 然是简洁,但是容易泛滥 (x?a:b)?(x?a:b):(x?a:(x?a:(...))).

Go不是因为 简洁 的 x?a:b 而禁止三元操作符,而是为了防止泛滥使用而禁止三元操作符.

1.14 编译生成的可执行文件尺寸非常大

C语言的0.04MB程序如果崩了(Windows64环境TDM-GCC生成128KB),你就只能知道它崩了.

而Go1.0的4MB程序如果崩了,你可以知道在哪个文件的哪行代码崩了,这就是差别!

对于Go1.5,Windows64环境,使用 fmt.PrintlnHello world 生成的 exe 有 2.4 MB.

对于 Rust1.1,生成的 exe 有 2.3 MB.

做了一个数组越界导致崩溃的测试,Go生成的2.4MB的程序可以输出导致崩溃的文件名和行号:

panic: runtime error: index out of range goroutIne 1 [running]: main.main() D:/path/to/main.go:7 +0x1b9

相当于CXX/C 加 -g -O 参数使生成文件含debug/trace信息,这样会增加文件大小。-w 去掉DWARF调试信息,得到的程序就不能用gdb调试了
不建议s和w同时使用。也可以压缩生成文件

Rust 生成的exe只能输出以下没啥用的信息:

thread @H_404_266@‘<main>‘ panicked at @H_404_266@‘index out of bounds: the len is 2 but the index is 100‘,C:/bot/slave/stable-dist-rustc-win-gnu-64/build/src/libcollections\vec.rs: 1359 

关于exe大小的问题可以关注 Issue6853.

1.15 不支持动态加载类库

假设系统由100多个exe组成了,那总共也就是不超过1GB的磁盘空间,没觉得有多大.

而且DLL依赖的地狱难道忘记了吗.

Go 1.8及以后支持plugin, 还可以玩玩hot-plugin,https://github.com/campoy/golang-plugins。

1.16 其他

  • 导入pkg的import语句后边部分竟然是文本(import ”fmt”)

  • 没有enum类型,全局性常量难以分类,iota把简单的事情复杂化

  • 定义对象方法时,receiver类型应该选用指针还是非指针让人纠结

  • 定义结构体和接口的语法稍繁,interface XXX{} struct YYY{} 不是更简洁吗?前面加上type关键字显得罗嗦。

  • 测试类库tesTing里面没有AssertEqual函数,标准库的单元测试代码中充斥着if a != b { t.Fatal(...) }。

  • 语言太简单,以至于不得不放弃很多有用的特性,“保持语言简单”往往成为拒绝改进的理由。

  • 标准库的实现总体来说不甚理想,其代码质量大概处于“基本可用”的程度,真正到企业级应用领域,往往就会暴露出诸多不足之处

  • 版本都发展到1.2了,goroutIne调度器依旧认仅使用一个系统线程。GOMAXPROCS的长期存在似乎暗示着官方从来没有足够的信心,让调度器正确安全地运行在多核环境中。这跟Go语言自身以并发为核心的定位有致命的矛盾。(直到2015年下半年1.5发布后才有改观)

  • 官方发行版中包含了一个Oracle的辅助程序,与Oracle数据库毫无关系,却完全无视两者之间的名称混淆。

  • 不支持函数重载减轻了读代码的负担,是好事情. 可变的a+b行为比可变的Add(a,b)难发现多了

  • import导入文本绝对是优点,因为可以支持很多以特殊字符命名的路径: import "_-aa/bb~/dd/xx",只有包名满足ID命名规则就可以了,前缀部分可以很随意

  • receiver 就是普通函数func(self T,...) 和 func(self *T,...) 的差别不是很明显吗

  • type 开始规则更统一,和 var x int 和 func Add(a,b int) int 类型后缀的规则是一致的(Rust中的变量和函数也是类型后置吧),比如 type MyInt inttype MyFunc func(...),而且也非常便于解析和查找(正则^type就可以定位了)

  • 如果要加 AssertEqual 的话,那么什么叫 equal 呢? 2个map或struct如何才是叫相等,chan成员呢? 别总是想着增加功能,增加功能的同时带来的问题和复杂性难道不需要虑吗?

  • 语言太简单难道不是优点吗? C++语言够复杂,建议楼主深入学习

  • 标准库的一大原则就是基本可用,标准库不是一个大杂烩,我想"少即是多"的哲学你是不会理解的

  • Go1.5认N个系统线程,N为cpu核心数目. 认值并不是没有信心,而是对于不同的程序,需要几个线程最好是一个比较困难的事情(比如gui程序为何不用多线程呢).

  • Oracle 就是一个普通的单词,为何不能使用? 楼主会不会因为买了水果的手机,以后就不认识 apple 这个单词了?

给Go提交的CL的要求是非常高,楼主的BOM提法我觉得可以讨论,但是不要以为CL增加了特性就必须得通过.

还好,Go团队没有接受你上面的诸多建议,要不然我估计我现在已经放弃Go了.

第2节:我为什么对Go语言的某些人不爽?

对于一,固执的G员工,你要通过逻辑来说服他们,如果自己都没有干货,别人凭什么要采纳你的建议(上面的绝大部分建议我就@L_675_136@)?

对于二,脑残粉丝谁都烦,希望楼主下次吐槽能给点干货,别把自己也整成了脑残粉.

对于技术而言,我更喜欢独裁者. 所谓的开源烂民主那是活稀泥的.

你可以尝试去提一个Linux内核也增加GUI模块的建议试试.

Go1.5的地基已经非常的牢固,这个你不用担心.

缺少使用Go语言的亲身经历,楼主也真的敢信口开河. 你不是以为G公司开发Go真的是用来玩的吧.

再说Go1.5已经完全没有C代码了,这下你该闭口了吧.

你的很多批评和改进意见都是狗屎(包括BOM那个). 你这样的用户没有融入社区时好事情,Go语言只要在生产环境好用就可以了.

可惜世界不是以你的意志改变的,Go还将继续快速发展,你是很难受吧?

第3节:还有比Go语言更好的选择吗?

不就是号称银弹的 Rust 吗,但是 然并卵. 我也断言一句: Rust 最终只能是小众语言,想代替 Go语言/C语言 根本是没戏的(Swift开源后基本可以秒杀Rust).

第4节:写在最后

走好,不送!

@H_403_846@Liigo 2014-4-29 补记1:

在工作中都已经用上了,你还在想象别人是在盲目推崇,是你自己在梦游吧.

Liigo 2014-4-29 补记2:

王的垠语言出来了吗? 等着10年后他再次扇自己的脸(参Windows无用那篇).

Liigo 2014-4-29 补记3:

不需要NB的特性,只需要简单/好用/实用就行.

@H_133_674@Liigo 2015-1-31 补记4:

就是数万个“赞”又怎么样? 关键是很多地方Go已经用起来了.

Liigo 2015-4-1 补记5:

Liigo 2015-5-29 补记6:

原来这是你的功劳!

Liigo 2015-6-2 补记7:

放弃Go语言很正常,也有很多放弃X语言投奔Go语言的例子.

关于对作者倾向性质疑的声明:

不需要NB的特性,只需要简单/好用/实用就行.

关于对作者阴谋论的声明:

CL被据而怀恨至今真的是没冤枉楼主,希望下次抹黑Go能来点干货.

当然Go语言也不是完美的,作为Windows下的Go用户,说下我比较希望的改进.

首先在下面的bug修复前,我并不十分关心Go的性能改进。

上面2个bug是支持go和c库双向合作的关键(Linux和Darwin已经支持生成动态库). 然后就是 cgo 调用 c 函数的参数传递的性能能改善下.

在go1.5中,新引入了 vendor 的试验性的特性,因此go的包依赖管理算是基本解决.

长远看希望语言方面能有以下的特性:

  • 范型支持,可以简陋些,但是不要破坏go已有的风格
  • 希望能有不支持嵌套的三元表达式的支持
  • 大小写的导出规则对中文能友好一些
  • 接口瘾式转换导致的一些坑(errornil)
  • 官方的leveldb库和基于其封装的sql数据库
  • os 的文件系统做成接口,提共自定义文件系统的挂载功能
  • image 包能增加 GrayA/GrayA32/RGB/RGB48 之类的类型支持
  • 性能改进

可有可无的:

https://chai2010.cn/

大佬总结

以上是大佬教程为你收集整理的驳2B文 "我为什么放弃Go语言"全部内容,希望文章能够帮你解决驳2B文 "我为什么放弃Go语言"所遇到的程序开发问题。

如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。