为什么我不会舍弃 Python 投奔 Go 语言

Russ Cox毕业于麻省理工和哈佛大学,曾在贝尔实验室参与过Plan 9 和 Unix操作系统的开发,最近在google,主要开发Go语言。

最近关于Go语言的话题很多,本月刚好是Go语言诞生3周年。这个语言在谷歌巨人的推动下最终很可能成为今后的主流语言。如果你想简单了解一下Go语言,这里有Go语言官方网站提供的一个很简单的中文版的“Go语言半小时速成教程”,不妨一看。

最近,由于一篇“为什么我不会舍弃Python投奔Go语言(英文)”的博客,我收到了大量的邮件,这篇文章中,作者说Go语言除了“用返回值来处理异常错误”这点外,其它的都非常好。我想写出一点东西,解释一下Go语言为什么这样做,这会对大家都有用。

在Go语言中,规定的方式是,函数返回错误信息。这没什么。如果一个文件并不存在,op.Open函数会返回一个错误信息。这没什么。如果你向你一个中断了的网络连接里写数据,net.Conn里的Write方法会返回一个错误。这没什么。这种状况在这种程序中是可以预料到的。这种操作就是容易失败,你知道程序会如何运行,因为API的设计者通过内置了一种错误情况的结果而让这一切显得很清楚。

从另一方面讲,有些操作基本上不会出错,所处的环境根本不可能给你提示错误信息,不可能控制错误。这才是让人痛苦的地方。典型的例子;一个程序执行x[j],j值超出数组边界,这才痛苦。像这样预料之外的麻烦在程序中是一个严重的bug,一般会弄死程序的运行。不幸的是,由于这种情况的存在,我们很难写出健壮的,具有自我防御的服务器——例如,可以应付偶然出现的有bug的HTTP请求处理器时,不影响其他服务的启动和运行。为解决这个问题,我们引入了恢复机制,它能让一个go例程从错误中恢复,服务余下设定的调用。然而,代价是,至少会丢失一个调用。这是特意而为之的。引用邮件中的原话:“这种设计不同于常见的异常控制结构,这是一个认真思考后的决定。我们不希望像java语言里那样把错误和异常混为一谈。”

我刚开始提到的那篇文章里问“为什么数组越界造成的麻烦会比错误的网址或断掉的网络引出的问题要大?”答案是,我们没有一种内联并行的方法来报告在执行x[j]期间产生的错误,但我们有内联并行的方法报告由错误网址或网络问题造成的错误。

使用Go语言中的错误返回模式的规则很简单:如果你的函数在某种情况下很容易出错,那它就应该返回错误。当我调用其它的程序库时,如果它是这样写的,那我不必担心那些错误的产生,除非有真正异常的状况,我根本没有想到需要处理它们。

有一个你需要记在心里的事情是,Go语言是为大型软件设计的。我们都喜欢程序简洁清晰,但对于一个由很多程序员一起开发的大型软件,维护成本的增加很难让程序简洁。异常捕捉模式的错误处理方式的一个很有吸引力的特点是,它非常适合小程序。但对于大型程序库,如果对于一些普通操作,你都需要考虑每行代码是否会抛出异常、是否有必要捕捉处理,这对于开发效率和程序员的时间来说都是非常严重的拖累。我自己做开发大型Python软件时感受到了这个问题。Go语言的返回错误方式,不可否认,对于调用者不是很方便,但这样做会让程序中可能会出错的地方显的很明显。对于小程序来说,你可能只想打印出错误,退出程序。对于一些很精密的程序,根据异常的不同,来源的不同,程序会做出不同的反应,这很常见,这种情况中,try + catch的方式相对于错误返回模式显得冗长。当然,Python里的一个10行的代码放到Go语言里很可能会更冗长。毕竟,Go语言主要不是针对10行规模的程序的。

Raymond Chen的文章是我读到过的最好的用来解释异常捕捉模式里问题的文章:

清晰,优雅,但却是错的

Cleaner, more elegant, and harder to recognize


就是要说明这一点:Go语言程序员认为,把error作为一种内置的类型是非常重要的。

Russ

附言

有时,你会发现,一种非本地的goto语句被当作错误恢复的方法,就像C语言里的longjmp 和 setjmp。这也不错,但最好只在内部用。如果调用者需要知道错误恢复的走向,那你的这种方式就不好了。

声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。

为什么我不会舍弃 Python 投奔 Go 语言