包名
当命名包时,请按下面规则选择一个名称:
全部小写。没有大写或下划线。
大多数使用命名导入的情况下,不需要重命名。
简短而简洁。请记住,在每个使用的地方都完整标识了该名称。
不用复数。例如net/url,而不是net/urls。
不是“common”,“util”,“shared”或“lib”。这些是不好的,信息量不足的名称。
函数名
我们遵循Go社区关于使用MixedCaps作为函数名的约定。有一个例外,为了对相关的测试用例进行分组,函数名可能包含下划线,如: TestMyFunction_WhatIsBeingTested。
包导入别名
如果程序包名称与导入路径的最后一个元素不匹配,则必须使用导入别名。
import (
“net/http”
client “example.com/client-go”
trace “example.com/trace/v2”
)
在所有其他情况下,除非导入之间有直接冲突,否则应避免导入别名。
Bad
import (
“fmt”
“os”
nettrace “golang.net/x/trace”
)
vs.
Good
import (
“fmt”
“os”
“runtime/trace”
nettrace “golang.net/x/trace”
)
函数分组与顺序
函数应按粗略的调用顺序排序。
同一文件中的函数应按接收者分组。
因此,导出的函数应先出现在文件中,放在struct、const和var定义的后面。
在定义类型之后,但在接收者的其余方法之前,可能会出现一个newXYZ()/ NewXYZ()。
由于函数是按接收者分组的,因此普通工具函数应在文件末尾出现。
Bad
func (s *something) Cost() {
return calcCost(s.weights)
}
type something struct{ … }
func calcCost(n int[]) int {…}
func (s *something) Stop() {…}
func newSomething() *something {
return &something{}
}
vs.
Good
type something struct{ … }
func newSomething() *something {
return &something{}
}
func (s *something) Cost() {
return calcCost(s.weights)
}
func (s *something) Stop() {…}
func calcCost(n int[]) int {…}
减少嵌套
代码应通过尽可能先处理错误情况/特殊情况并尽早返回或继续循环来减少嵌套。减少嵌套多个级别的代码的代码量。
Bad
for _, v := range data {
if v.F1 == 1 {
v = process(v)
if err := v.Call(); err == nil {
v.Send()
} else {
return err
}
} else {
log.Printf("Invalid v: %v", v)
}
}
vs.
Good
for _, v := range data {
if v.F1 != 1 {
log.Printf("Invalid v: %v", v)
continue
}
v = process(v)
if err := v.Call(); err != nil {
return err
}
v.Send()
}
不必要的else
如果在if的两个分支中都设置了变量,则可以将其替换为单个if。
Bad
var a int
if b {
a = 100
} else {
a = 10
}
vs.
Good
a := 10
if b {
a = 100
}
顶层变量声明
在顶层,使用标准var关键字。请勿指定类型,除非它与表达式的类型不同。
Bad
var _s string = F()
func F() string { return “A” }
vs.
Good
var _s = F()
// 由于F已经明确了返回一个字符串类型,因此我们没有必要显式指定_s的类型
func F() string { return “A” }
如果表达式的类型与所需的类型不完全匹配,请指定类型。
type myError struct{}
func (myError) Error() string { return “error” }
func F() myError { return myError{} }
var _e error = F()
// F返回一个myError类型的实例,但是我们要error类型