# 简介 Go是一个新的语言。虽然是借鉴了现有的语言,但是它独有的特性可以使得高效的Go程序,与其它语言编写的程序相比,大不相同。直接将C++或者Java 程序转换为Go程序,是不可能产生令人满意的结果—Java程序是使用Java编写的,而不是Go。另一方面,从Go的角度考虑问题则会产生成功的,而且 大不相同的程序。换句话说,想要编写好的Go程序,理解它的特性和风格是非常重要的。了解Go语言编程中已有的约定也非常重要,例如命名,格式,程序结 构,等等。这会使得其他Go程序员容易理解你编写的程序。 该文档对如何编写清晰,符合语言规范的Go代码,给出了一些建议。你应该先阅读language specification,Tour of Go和How to Write Go Code,然后将该文档作为扩展阅读。 ## 例子 Go package sources旨在不仅作为核心库来使用,而且还可以作为如何使用语言的例子。此外,许多程序包都包含了可以在golang.org网站上独立执行的例子,例如这一个(如果需要,点击单词"Example"来打开)。如果你对如何处理一个问题,或者如何进行实现有疑问,那么库中的文档,代码和例子可以提供答案,概念和背景。 ## 格式 格式化是一个最具争议,但又无关紧要的问题。人们可以习惯于不同的格式风格。但是,最好不必这样,这就不用在每个人是否遵守相同风格的话题上花费时间了。问题是在没有一个长效的风格指导下,如何达到这样美好的乌托邦。 对于Go,我们采取了不同寻常的方式,让机器来处理大多数的格式问题。程序gofmt(也可以用go fmt,其操作于程序包的级别,而不是源文件级别),读入一个Go程序,然后输出按照标准风格缩进和垂直对齐的源码,并且保留了根据需要进行重新格式化的注释。如果你想知道如何处理某种新的布局情况,可以运行gofmt;如果答案看起来不正确,则需要重新组织你的程序(或者提交一个关于gofmt的bug),不要把问题绕过去。 举个例子,不需要花费时间对结构体中每个域的注释进行排列。Gofmt将会替你完成这些。给定一个声明 ``` type T struct { name string // name of the object value int // its value }``` gofmt将会按列进行排列: ``` type T struct { name string // name of the object value int // its value }``` 标准程序包中的所有Go代码,都已经使用gofmt进行了格式化。 还是有一些格式化的细节的。非常简短: 缩进 我们使用tab进行缩进,这是gofmt的缺省输出。只有在你必须的时候才使用空格。 行长度 Go没有行长度限制。不必担心会有打孔卡片溢出。如果感觉一行太长,可以折成几行,并额外使用一个tab进行缩进。 括号 Go相比C和Java,很少需要括号:控制结构(if,for,switch)的语法不需要括号。而且,操作符优先级更短,更清晰。这样, ``` x<<8 + y<<16``` 的含义就已经由空格表明了。这不像其它语言。 ## 注释 Go提供了C风格的块注释/* */和C++风格的行注释//。通常为行注释;块注释大多数作为程序包的注释,但也可以用于一个表达式中,或者用来注释掉一大片代码。 程序—同时又是网络服务器—godoc,用来处理Go源文件,抽取有关程序包内容的文档。在顶层声明之前出现,并且中间没有换行的注释,会随着声明一起被抽取,作为该项的解释性文本。这些注释的本质和风格决定了godoc所产生文档的质量。 每个程序包都应该有一个包注释,一个位于package子句之前的块注释。对于有多个文件的程序包,包注释只需要出现在一个文件中,任何一个文件都可以。包注释应该用来介绍该程序包,并且提供与整个程序包相关的信息。它将会首先出现在godoc页面上,并会建立后续的详细文档。 ``` /* Package regexp implements a simple library for regular expressions. The syntax of the regular expressions accepted is: regexp: concatenation { '|' concatenation } concatenation: { closure } closure: term [ '*' | '+' | '?' ] term: '^' '$' '.' character '[' [ '^' ] character-ranges ']' '(' regexp ')' */ package regexp``` 如果程序包很简单,则包注释可以非常简短。 ``` // Package path implements utility routines for // manipulating slash-separated filename paths.``` 注释不需要额外的格式,例如星号横幅。生成的输出甚至可能会不按照固定宽度的字体进行展现,所以不要依靠用空格进行对齐—godoc,就像gofmt,会处理这些事情。注释是不作解析的普通文本,所以HTML和其它注解,例如_this_,将会逐字的被复制。对于缩进的文本,godoc确实会进行调整,来按照固定宽度的字体进行显示,这适合于程序片段。fmt package的包注释使用了这种方式来获得良好的效果。 根据上下文,godoc甚至可能不会重新格式化注释,所以要确保它们看起来非常直接:使用正确的拼写,标点,以及语句结构,将较长的行进行折叠,等等。 在程序包里面,任何直接位于顶层声明之前的注释,都会作为该声明的文档注释。程序中每一个被导出的(大写的)名字,都应该有一个文档注释。 文档注释作为完整的语句可以工作的最好,可以允许各种自动化的展现。第一条语句应该为一条概括语句,并且使用被声明的名字作为开头。 // Compile parses a regular expression and returns, if successful, a Regexp // object that can be used to match against text. func Compile(str string) (regexp *Regexp, err error) { 如果都是使用名字来起始一个注释,那么就可以通过grep来处理godoc的输出。设想你正在查找正规表达式的解析函数,但是不记得名字“Compile”了,那么,你运行了命令 $ godoc regexp | grep parse 如果程序包中所有的文档注释都起始于"This function...",那么grep将无法帮助你想起这个名字。但是,因为程序包是使用名字来起始每个文档注释,所以你将会看到类似这样的信息,这将使你想起你要查找的单词。 $ godoc regexp | grep parse Compile parses a regular expression and returns, if successful, a Regexp parsed. It simplifies safe initialization of global variables holding cannot be parsed. It simplifies safe initialization of global variables $ Go的声明语法允许对声明进行组合。单个的文档注释可以用来介绍一组相关的常量或者变量。由于展现的是整个声明,这样的注释通常非常肤浅。 // Error codes returned by failures to parse an expression. var ( ErrInternal = errors.New("regexp: internal error") ErrUnmatchedLpar = errors.New("regexp: unmatched '('") ErrUnmatchedRpar = errors.New("regexp: unmatched ')'") ... ) 分组还可以用来指示各项之间的关系,例如一组实际上由一个互斥进行保护的变量。 var ( countLock sync.Mutex inputCount uint32 outputCount uint32 errorCount uint32 ) 名字 和其它语言一样,名字在Go中是非常重要的。它们甚至还具有语义的效果:一个名字在程序包之外的可见性是由它的首字符是否为大写来确定的。因此,值得花费一些时间来讨论Go程序中的命名约定。 程序包名 当一个程序包被导入,程序包名便可以用来访问它的内容。在 import "bytes" 之后,导入的程序包便可以谈到bytes.Buffer。如果每个使用程序包的人都可以使用相同的名字来引用它的内容,这会是 很