最近在用 Golang ,感觉有很多槽点。王垠曾经对 Golang 做过一番批判,我自己用的时候也感觉到了很多问题,在此我严正批评一下 Golang 。

许多 Golang 的拥护者会称 Golang 是一门工程语言,他们有一种误解,即是工程问题是代码问题。但事实是,工程问题是一个现实的逻辑问题,而代码问题是代码的管理问题。 Golang 并没有解决很多逻辑问题,它是砍去了很多特性,比如说循环依赖,它觉得不好,砍了; try catch 机制;不好,砍了,泛型,不好,砍了,现在又偷工减料的加回来了。如果一个特性将会带来很多,一般的处理方法有两种,一种是引导人们更好的使用它,另一种是砍掉。Rust 选择了前者,而 Golang 则选择了后者。这固然是一种解决办法,但解决不了实际问题,更何况砍掉特性只会让语言的表达能力变弱,最终,语言会变得简陋而不是简单。

因此, Golang 不是一门简单的语言,并不在于语法不简单,而在于它先天约定了很多写法,需要你额外去学习。但需要这些写法的原因在于, Golang 缺乏处理这些问题特性,又同时缺乏对这些问题进行通用化解决的能力,导致约定的代码模板多于语法本身的现象出现。

我举个例子,比如说循环依赖。我们都说循环依赖是不好的,但真的如此吗?架构上的循环依赖,一部分考虑是架构上设计的不合理,但另一部分可能是现实逻辑要求实体间必然存在循环依赖。前者的话仅仅通过架构调整即可解决,但后者却要改变现实逻辑,这是不现实的。虽然说循环依赖是不好的,但在 Golang 中大家普遍通过接口来解决循环依赖,虽然代码层面的循环依赖没有了,但在现实逻辑中的依赖问题并没有解决,且通过一个接口进行转嫁。我们似乎完成了一个很好的解耦,但事实上这种解耦破坏了现实的逻辑,使得实体间逻辑增加,未必比循环依赖要来得简单。但除此之外, Golang 也没有更多的解决方案,面对纷繁复杂的现实世界, Golang 的表现力严重不足。

又比如说泛型,泛型问题暴露出了 Golang 在多态方面的孱弱,而且它也没有什么宏或者什么好的元编程手段,导致它在这方面的表现力甚至不如 C 。泛型的缺失迫使得程序员不得不书写大量的重复逻辑,在一些算法上,进行着一些毫无意义的重复。好在 Golang 官方还是能认识到这一点的,虽然新给的蚊子腿又小又贫瘠,但勉强还是有的。 Golang 还是要重新认识一下多态的价值。

Golang 就像是一个阉割版的 C ,同时又像模像样的加上了一些特性,比如说 GC ,又比如说 map 。但 map 都给了却又不好好给一下其他的容器类型,好像 Go 程序员通天遁地,随时准备着手撸代码一样。而且 Golang 蠢笨的设计迫使程序员不得不学习更多特殊的写法,以获取在别的语言伸手可得的特性,但这些额外的学习成本并不会在一开始就展示给你,你不得不一个坑一个坑的踩。而那些已经踩过坑的程序员总是得意洋洋的在坑外嘲讽你,仿佛这些坑天然合理,不会跳过的人就是蠢笨一样。

Golang 对于一门高级语言来说,拒绝了多态,不能够进行更高层次的抽象;对于像 C 一样的低级语言来说,底层的敞开不够, GC 抹除了过多细节, goroutine 又将整个程序的细节封装得过于严实,而它的指针又不是自由的,不能自由操作内存,完全无法将自己精确把握到汇编级。 Golang 就是这样的尴尬,高不成低不就,最适合 Golang 的场景大概就是需要底层但又不需要过多了解细节,也不需要过度抽象的场景,比如说聊天协议等等,可以替代一部分 C 抽象不足的工作,但距离一般的通用语言始终有差距。