4、Go编码规范
“代码必须是本着写给人阅读的原则来编写,只不过顺便给机器执行而已。”
=========》这段话来自《计算机程序设计与解释》
代码风格,是一个与人相关、与机器无关的问题。代码风格的好坏,不影响编译器的工作,但是影响团队协同,影响代码的复用、演进以及缺陷修复。
Go 语言很可能是第一个将代码风格强制统一的语言。一些对于其他语言的编译器完全忽视的问题,在 Go 编译器前就会被认为是编译错误,比如如果花括号新起了一行摆放,你就会看到一个醒目的编译错误。
这一点会让很多人觉得不可思议。无论喜欢还是讨厌,与其他那些单单编码规范就能写出一本书的语言相比,毫无疑问 Go 语言的这种做法简化了问题。我们接下来介绍 Go 语言的编码规范,主要分两类,分别:由 Go 编译器进行强制的编码规范、由 Gotool 推行的非强制性编码风格建议。
强制性编码规范
Go 编译器进行强制的编码规范也是 Go 语言设计者认为最需要统一的代码风格。
1. 命名
命名规则涉及变量、常量、全局函数、结构、接口、方法等的命名。Go 语言从语法层面进行了以下限定:
任何需要对外暴露的名字必须以大写字母开头,不需要对外暴露的则应该以小写字母开头。
软件开发行业最流行的两种命名法分别为:
- 骆驼命名法(类似于
DoSomething
和doSomething
) - 下划线法(对应为
do_something
)
而 Go 语言明确宣告了拥护骆驼命名法而排斥下划线法。骆驼命名法在 Java 和 C# 中得到官方的支持和推荐,而下划线命名法则主要用在 C 语言的世界里,比如 Linux 内核和驱动开发上。在开始 Go 语言编程时,还是忘记下划线法吧,避免写出不伦不类的名字。
2. 排列
Go 语言甚至对代码的排列方式也进行了语法级别的检查,约定了代码块中花括号的明确摆放位置。下面先列出一个错误的写法:
// 错误写法
func Foo(a, b int)(ret int, err error)
{
if a > b
{
ret = a
}
else
{
ret = b
}
}
这个写法对于众多在微软怀抱里长大的程序员们是最熟悉不过的了,但是在 Go 语言中会有编译错误:
$ go run hello.go
# command-line-arguments
./hello.go:9: syntax error: unexpected semicolon or newline before {
./hello.go:18: non-declaration statement outside function body
./hello.go:19: syntax error: unexpected }
通过上面的错误信息就能猜到,是左花括号{
的位置出问题了。下面我们将上面的代码调整一下:
// 正确写法
func Foo(a, b int)(ret int, err error) {
if a > b {
ret = a
} else {
ret = b
}
}
可以看到,else
甚至都必须紧跟在之前的右花括号}
后面并且不能换行。Go 语言的这条规则基本上就保证了所有 Go 代码的逻辑结构写法是完全一致的,也不会再出现有洁癖的程序员在维护别人代码之前非要把所有花括号的位置都调整一遍的问题。
非强制性编码风格建议
package main
import "fmt"
func Foo(a, b int)(ret int, err error){
if a > b {
return a, nil
}else{
return b, nil
}
return 0, nil
}
func main() {
i, _ := Foo(1, 2)
fmt.Println("Hello, 世界", i)
}
这段代码能够正常编译,也能正常运行,只不过丑陋的让人看不下去。现在我们用 Gotool 中的格式化功能美化一下(假设上述代码被保存为hello.go
):
$ go fmt hello.go
hello.go
执行这个命令后,将会更新hello1.go
文件,此时再打开hello1.go
,看一下旧貌换新颜的代码。
可以看到,格式化工具做了很多事情:
- 调整了每条语句的位置
- 重新摆放花括号的位置
- 以制表符缩进代码
- 添加空格
当然,格式化工具不知道怎么帮你改进命名,这就不要苛求了。
这个工具并非只能一次格式化一个文件,比如不带任何参数直接运行go fmt
的话,可以直接格式化当前目录下的所有*.go
文件,或者也可以指定一个 GOPATH 中可以找到的包名。
只不过我们并不是非常推荐使用这个工具。毕竟,保持良好的编码风格应该是一开始写代码时就注意到的。第一次就写成符合规范的样子,以后也就不用再考虑如何美化的问题了。
gofmt 介绍
Go 语言的开发团队制定了统一的官方代码风格,并且推出了 gofmt
工具(gofmt
或 go fmt
)来帮助开发者格式化他们的代码到统一的风格。
gofmt
是一个 cli 程序,会优先读取标准输入,如果传入了文件路径的话,会格式化这个文件,如果传入一个目录,会格式化目录中所有 .go
文件,如果不传参数,会格式化当前目录下的所有 .go
文件。
gofmt
默认不对代码进行简化,使用 -s
参数可以开启简化代码功能,具体来说会进行如下的转换:
go fmt 使用
go fmt -w xx.go
package main
import "fmt"
func main() {
a := 1
b := 2
c := a + b
fmt.Println(c)
}
用以下规则来格式化上面的代码。
gofmt -w -r "a + b -> b + a" main.go
package main
import "fmt"
func main() {
a := 1
b := 2
c := b + a
fmt.Println(c)
}
注意:go fmt
使用 tab
来表示缩进,并且对行宽度无限制,如果手动对代码进行了换行, gofmt
不会强制把代码格式化回一行。
-l
:显示那些需要格式化的文件-w
:把改写后的内容直接写入到文件中,而不是作为结果打印到标准输出。-r
:添加形如 “a[b:len(a)]->a[b:]
” 的重写规则,方便我们做批量替换-s
:简化文件中的代码-d
:显示格式化前后的 diff 而不是写入文件,默认是false
-e
:打印所有的语法错误到标准输出。如果不使用此标记,则只会打印不同行的前 10 个错误。-cpuprofile
:支持调试模式,写入相应的cpufile
到指定的文件