[Go] 学习Go语言20天的一些吐槽

陪她去流浪 桃子 2017年11月25日 编辑 阅读次数:3510

文章声明:本文仅是一个学习Go语言20天的家伙的一面之词,带有强烈的主观色彩。如果您正在考虑学Go语言的时候遇到了这篇文章,请相信自己。

从本月的5号开始,学习Go语言(Golang)已经20天了。想简单谈一谈感受。

这20天,我一直抽空余时间在阅读Go语言相关的书籍与教程。最先看的来自Go语言官方的「Go语言圣经」(The Go Programming Language),中文版,快速翻了一遍,没有认真仔细地看,周末两天就翻完了。然后看完了官方的入门教程「A Tour of Go」。再然后就是做完了GobyExample 上面的示例程序。再再然后就是花了一天时间写了一个本地代理服务器,两天时间写了一个隧道代理服务器(代码也就300行吧),功能大家都懂的。

总体来说,Go语言不算复杂。跟着官方的示例教程走一遍就能大概了解这门语言的整体了。当然,说不复杂也是好也是坏。好处是:上手快。坏处是:做大型项目可能不太适合。

本文的主要目的是想通过最近几天写的这个代理工具来简单对Go语言进行一些毫无营养的吐槽。当然,一门语言不可能被所有的开发者所喜欢、所接受,也不能适用于所有的场景。吐槽完了,代码还是继续写。

吐槽开始

不允许左花括号另起一行

非常多的人表示对这个不爽。不过我表示无压力。几年前开始,除了函数的花括号习惯写在单独的行上外,其它的像是 iffor 都是写在同一行。

写到这里,我想到的我的上一家公司我的上级老大,他是坚决反对花括号写在同一行的。我表示很有压力。

没有警告,只有错误

在Go语言里面导入包时,如果某个被导入的包没有被使用,那将会是一个错误,抱歉,你一定不能编译通过。

变量也是同样,如果某个变量被定义了但没有使用,不行,你同样不能编译通过。

简直了,这经常会让开发者崩溃~~~~~~~~~~~~~~~··················

我看到有人说这样很好,因为可以避免把未使用的东西编译进目标文件中,减小目标文件体积。我擦,这什么逻辑?明明知道某个包没有使用、某个变量没有使用,你为何还要莫名其妙地编译进目标文件?根本不能自圆其说。

相当原始的错误处理机制

原始到什么程度?大概跟C语言一样:每调用一个函数,你都很可能要检查一下它的返回值是否正确,然后下一步。,因为它没有类似C++/Java语言里面的异常处理机制。

用我在网上看到某人的说法:在Go语言里面,你写3行代码,可能就需要一个错误判断。

实现接口不需要明确声明

如果某个类型实现了签名相同的函数,那就代表它实现了某接口。根本不需要声明。

不需要声明,就意味着,编译器也无法知道你到底是不是真正的想实现某个接口。如果将来接口定义改变了,编译也不会抱错。因为根本没有明确规定。也就是失去了编译器的检查,把错误处理交给了运行时,得不偿失。

并且同样由于这个原因,如果需要同时实现的两个接口具体签名相同的函数,但是实现却不一样。那就不尴尬了。好像Go语言的作者认为这种情况并不常见。

想想C++语言,如果多重继承的基类具体相同签名的函数怎么办?你需要另外写两个类来转发,我艸?!你为什么就学不会C#语言的显示声明

C++语言标准的人一向固执己见(程序员能解决的不要我编译器来解决)并且年老可以理解,但是Go这么年轻就处理不好这些问题。这是我认为它无法开发大型程序的一个原因。

Google当初为什么把Python的作者辞退了?貌似就是因为Python无法开发大型程序。动态语言嘛,开发起来一时爽,代码重构火葬场。

生成的目标可执行文件尺寸文件非常大

写个一句 Hello World,目标文件可能有好几兆,很多人无法忍受。想想在C语言里面,可能就2KB。

不过我表示可以接受。因为,Go编译器可以跨平台编译生成其它平台的代码,而且默认非常少的依赖,这非常好。

没有枚举类型,只能常量代替

没有枚举意味着没有名字空间控制,因此,所有的全局常量定义在同一个名字空间内。所以你不得不为它们都加上一个前缀。简直疯了。

定义方法时不能指定对象为常量对象

如果不允许某个方法修改成员数据的话,应该传入一个常量对象。

但是Go并不支持,你需要传拷贝过去。好弱智的做法(的确弱智,并非dis别人来抬高自己)。

罗嗦的变量定义语法

定义一个变量需要这样:

1
2
3
    var s   string
    var i   int
    var b   bool

我去,直接:

1
2
3
    string  s
    int     i
    bool    b

不好?非要非得这么罗嗦?

结构体和接口也是类似:

1
2
3
4
5
6
7
    type xxx struct {

    }

    type yyy interface {

    }

哎,下面这样不更简洁?

1
2
3
4
5
6
7
    struct xxx {

    }

    interface yyy {

    }

知乎上有针对此的讨论,有人说是因为编译器为了追求速度的原因,所以这样。

但被轮子哥(vczh)等人否定了。我表示无法理解。

没有条件运算符

C++ 中有 int n = a ? b : c; 的写法,但是Go却没有,你需要下面这样写:

1
2
3
4
5
6
var n int
if a {
    n = b
} else {
    n = c
}

简直了,神经病。

所有源代码需要放在同一个目录下

是的,看看官方的包也都是这样。无法想像。

无法共享源代码文件

我写前面提到的代理工具时遇到一个问题。服务端和客户端需要同一份结构体的定义。但是Go并不支持导入这种相对路径的依赖文件。需要把它们做成一个包。。。。。。。。。。。

做成包后,又必须带上新的包名前缀,我擦,吃屎去吧。

这让我想起了Python的语法。

1
2
3
import xxx
from xxx import yyy
from xxx import *

这样就很好啊,但是Go,学不会。

吐槽结束

无营养的吐槽,随便看看就好了。

参考:

标签:Go