《我不喜欢Go语言的十个理由》 by Lawrence 2019/06/17
《我不喜欢“我不喜欢 Go 语言的十个理由”的十个理由》 by Wzy_CC 2020/07/08
我不喜欢Go语言的十个理由作者Lawrence使用了三年Go语言,并且参与了多个大型Go的项目。但是他现在放弃了Go并且认为Go“在不好的方面令人无法忍受”。但是其中一些理由我认为有点牵强,View1-10分别是作者的十个不喜欢Go语言的理由:
View1
Go 语言使用首字母大小写来决定标识符的可见性
对包外暴露的变量和方法使用大小写来区分公私有,这其实是非常聪明的做法,不但减少了关键字的个数,而且使得整体看起来简介优雅。
如果从java或者C++转为go开发,自然会对缺少public
和private
关键字而不适应,对于作者认为的首字母大写应该“含有其他含义”,对此go语言规范中对于包名,函数名和变量名都认为应当使用小写,则此时大写的方法和变量自然暴露在包外提供访问,常量大写是惯例,依旧由const
关键字进行区分,并不是太大的问题
作者认为下列代码因为小写字母而变得糟糕:
type user struct {
name string
}
func main() {
var user *user
user = &user{} // Compile error
}
上述代码中user是私有结构体,而结构体的变量则是user,在编译中产生错误,把user类型当作user变量而无法通过编译。事实上这也并不是大问题。任何这种随意起变量名而不加以区分不带有实际意义的声明,在任何语言的代码中都是极其糟糕的。
命名方式是开发者的习惯,如果因为核心开发成员中有人习惯短命名的方式而攻击语言的话,是不是本末倒置了一点。
View2
结构体不会显式声明了它实现了哪些接口,而是通过匹配方法签名来鉴别
Go本身没有类,说什么你都对。
View3
Go语言没有异常,而是通过多个返回值来返回错误
这恰恰时Go区别于其他语言的一大特性,他要求开发者必须关注并显式处理可能存在的错误,而且作者错误的认为可以将err赋值给_,这种错误处理方式正是所提倡的提醒开发者,而不会使开发者”忘记“检查返回值中是不是包含了错误,完全属于开发者自己的代码不够健壮,而非语言本身的缺陷。
作者在下面代码中认为,并不保证user和err包含正确的值,user可能没有被赋值
user, err := getUserById(userId)
超出我的理解,也不能明白在其他语言中未被赋值可以通过某种方式检查出来而不依赖反射Valueof
等方法?
View4
Go代码的神奇行为
作者认为凑巧把一个函数名定义为init()
则会在运行时自动调用,恰恰相反,在诸多go的最佳实践中都使用了init()
函数,且在需要的时候显式调用,这不是神奇的行为,而属于对包或者框架内函数的运行方式不熟悉造成的误解。
View5
因为首字母大写约定,很容易出现很多相同的标识符
”一些包名、结构体名和变量名都叫作 item。在 Java 中,包名使用了全限定名,类名首字母是大写的。有时候觉得 Go 代码不好阅读,因为可能无法一下子看出一个标识符的作用域是怎样的。“
结构体变量名在同一个包内的重名在编译时就无法通过,至于包名别称和变量的重名属于开发习惯问题。
View6
Go代码通过编译并不容易
编译器太过严格从对开发者的要求上是好事,对于开发者的代码水平来说,越严格的编译器代码质量越高,这绝对是go的优良特性之一。
View7
Go语言没有三元运算符(?:)
没有三元运算,而转换为了繁杂的分支语句,的确变得不优雅了
View8
sort接口很笨
自定义类型实现sort接口需要三个函数,而当存在十个结构体时则必须写30个函数,作者认为其中二十个是一样的。这是因为go缺少泛型,类比C++如果不存在模板,则实现排序同样是一个灾难。
作者认为转型看起来像是一个函数:
sort.Sort(sort.Reverse(UsersByLastSignedInAt(users)))
事实上理解为函数传递对于理解也没有任何问题。
View9
缺少泛型
增加泛型是社区呼声最高的声音,事实上在即将到来的2.0版本中会加入Go泛型。
View10
没有站在程序员的角度考虑问题
作者列举了一个append函数的例子,其中append返回一个新数组:
users = append(users, newUser)
而作者认为下面这行代码总能调用成功是最糟糕的:
append(users, newUser)
因为函数在可能的情况下进行原地数组修改,如果没有足够的空间就返回一个新数组。而这个糟糕的设计在最新的编译器中已经修改了,当append
函数不作为右值时在编译时无法通过。
OtherView
HackerNews社区其他的一些声音
在项目中未导入的包和变量会导致编译错误,强制给编译器打补丁忽视错误是本质上的倒退,既然没有使用为什么需要声明呢,而这正式Go比PHP更容易维护的原因
如果你要对一个片段进行排序,sort.Slice
比sort.Interface
更好
任何一门语言中都不应该定义容易产生混淆的接口
总结
语言没有好坏,全看开发者喜不喜欢,业务需不需要。是否选择一门语言,不要看它的设计,要看它是否可以解决你的问题。
开发者喜欢的语言对于开发者来说当然是“好“的语言,而作者可能习惯于java的一些使用方法,且由于go糖较少而使用不习惯,语法糖不是必需品。
关于是否应该在go中使用init()
函数,详见《现代Go的一些理论》