blogWithMarkdown icon indicating copy to clipboard operation
blogWithMarkdown copied to clipboard

小试Go

Open spacewander opened this issue 10 years ago • 0 comments

作为一个有志于开发服务端程序的人,如果没有尝试过Go,那实在是太可惜了。抱着这样的念头:),最近尝试下了Go。敲了些能找到的例子,不过目前暂时既没有用Go开发过side project,也没有给Go写的开源项目贡献代码(等开始结课后,时间变得更加充裕才继续)。刚刚统计了下,大致累积了三千多四千行。我觉得应该可以给Go一个阶段性判断了。

Go作为一门专为服务器端并发编程而生的系统编程语言,果然不负众望。goroutine和channel珠联璧合,编译速度更是出乎意料地快,非侵入接口实现了静态语言版Duck type。但是,由于没有泛型,有些设计真让人啼笑皆非。另外,其错误处理的机制也让我有点不习惯。总的来说,要想实现Go原初的目标 - 替代C - 还有很长一段路要走。

写啊写

Go作为Google的side project,也毫不意外地享受到GFW的“一视同仁”。golang主页如果不翻墙,是打不开的。关于golang的另一个资料来源,Google邮件组中的Go-nuts,也是得在墙外世界中。总之,如果你想学Go,那么翻墙吧。

现代编程语言往往提供all in one的工具链,方便用户进行编译/测试/打包/管理依赖等一系列工作。你可以尝试从Go China的国内镜像中获取,也可以使用包管理。包管理版本比较旧(我这里是Go 1.2,而最新是Go 1.4),但是安装方便,输完一行命令之后就会自动安装好了。鉴于本人比较懒,而且又不是狂热的Go fan,无需追求最新版,所以是使用包管理安装的。

开发工具直接使用vim + vim-go就好。Go开发非常轻便,无需一个沉重的IDE处理各种琐事。Keep it simple stupid!

困扰

Go的目标是Better C。所以它真的是挺simple的,而且跟C一样,也没有什么语法糖。嗯,这些也算是意料之中啦。只是Go的编译速度太快了,总是让人产生它是一门解释型语言的错觉。

跟其他的现代编程语言一样,Go也是把参数类型置于参数名字之后的。比如i int(v []int, cp func(int, int) bool) bool。一开始好不习惯,在写了一段时间后才适应了这种新规。即使这样,我还是觉得v int[]这种写法要比v []int更加合理。毕竟v是int类型的数组,而不是数组,int类型的。

Go还有一个坑,slice操作返回的是数组的一部分引用,而不是新的数组。我还是第一次碰到这么设计的,果不其然就掉到这个坑里。(我在微博吐槽过这件事)

Go原生支持UTF-8,只是支持的方式比较特别。由于UTF-8字符不是等长的,所以你不能通过按byte访问来正确获取UTF-8字符串中的各个字符。Go使用“码点”来解决这个问题。如果使用for...range循环来访问一个字符串,你就能访问到每一个UTF-8字符。如果你使用for i = ...; i < ...; i++,就是按byte访问。想要获得各个“码点”的顺序?先把字符串强转成~~符文数组~~[]rune,就可以以for i = ...; i < ...; i++访问各个“码点”了。Go这种做法,兼顾了UTF-8支持和底层操作的能力,其实挺好的。这样就完了吗?没有。len(str)返回的是字符串的byte数目。如果你想获取字符串的真正长度,需要先转换成[]rune,再取其大小。又一个令人困惑的地方。

接下来是时候总结下Go的优缺点了。鉴于前面吐槽了一堆,我还是先说优点吧。

优点

Go的编译速度实在太快了!以至于让人产生它是解释型语言的错觉。直接go run,一下子就能把程序运行起来。鉴于Go的工具链这么完备,且编译-运行周期这么短,同时不缺乏底层操作,完全可以作为大学的入门语言(如果不考虑就业的话),也可以作为拉小正太小萝莉入坑的语言(如果不考虑就业的话)。

另外,Go提供的非侵入接口也是一大创举(不知道是不是Go第一个提出来呢?)。这样子静态语言也能有Duck type。我觉得这种做法大有前景,将来应该会流行开来。

当然,提Go,绝对离不开其打天下的杀手锏 - goroutine和channel这对宝具。正是Go,让CSP并发模型名扬四海、妇孺皆知(大误)。 提CSP,就不能不提actor。这一对并发模式挺像的,只是前者关注于传递信息的通道(channel),后者关注于传递信息的对象(actor)。如果对这两种并发模式感兴趣,建议看下《Seven Concurrency Models in Seven Weeks》。鉴于这是在讲Go而不是讲并发,我一笔带过这部分好了:actor优点在于let it crash的容错机制。CSP优点在于更加关注于通信的渠道而非通信的对象,避免发送信息过多造成信息队列丢弃信息(在CSP中,这会导致阻塞),也避免发送信息的过程中由于对方节点崩溃而额外带来的错误处理。反正本人更青睐于CSP。

在Go中搞并发非常轻松。有多轻松?这的确很难量化,你也来试下,不就知道了?

缺点

Go的开发者们并不喜欢异常。即使异常已经成为主流编程语言不可或缺的一部分,连C++也开始拥抱异常了。但是C里面没有异常,作为better C,Go里面何须使用异常呢?所以Go还是沿用C的那一套。C是使用错误编码来报告出错的,但是一来调皮捣蛋的猴子们往往会无视返回值的处理,二来错误编码非常令人困惑,你得对着文档才能逐个处理好。所以作为better C,Go对此做了改进。Go返回Error对象报告错误,你可以子类化该对象来定义属于自己的错误编码。Error和结果一齐返回,这么一来猴子们就不会忘记使用if xxx; err != nil {来处理错误了。尽管如此,错误码永远比不上异常。你可以让高层次的代码处理异常,但是对于错误码,要想这么做,你得认真仔细地设计其调用体系。这样就对设计增加了额外的负担。你说用panicrecover?那又不能根据不同的异常情况进行不同的处理。

另外一个让人无法容忍的是,Go直到现在还是不支持泛型。之前看过一个Go社区的讨论,有人比较了C/C++/Java三者中实现泛型的方式。

...C选择麻烦程序员,C++选择麻烦编译,Java选择麻烦运行效率

所以到现在,Go还是没有决定好要麻烦谁,一直都没有推出泛型。在我认识的现代静态语言中,Go应该是唯一不支持泛型的。 所以有了这样的库函数设计:

Sort.Ints
Sort.Strings
Sort.Float64s
...

对,你没看错,Go针对每种基本类型实现了一次sort函数! 而且,因为没有泛型,Go里面不存在通用的max/min操作。由于Go里面也没有三元比较符,为了实现max,你需要这么写:

max typeX
if compare(a, b) > 0 {
    max = a
} else {
    max = b
}

值得一试

最后,虽然Go不能成为我心目中最喜欢的语言,但是Go还是值得一试的。我也会推荐别人尝试下Go。Go现已加入我的豪华套餐 >_< 。

spacewander avatar Apr 21 '15 13:04 spacewander