Uber Go语言编码规范( 六 )

Bad
if x == "" { return []int{}}vs.
Good
if x == "" { return nil}

  • 要检查切片是否为空 , 请始终使用len(s) == 0 。不要检查 nil 。
Bad
func isEmpty(s []string) bool { return s == nil}vs.
Good
func isEmpty(s []string) bool { return len(s) == 0}
  • 零值切片可立即使用 , 无需调用make创建 。
Bad
nums := []int{}// or, nums := make([]int)if add1 { nums = append(nums, 1)}if add2 { nums = append(nums, 2)}vs.
Good
var nums []intif add1 { nums = append(nums, 1)}if add2 { nums = append(nums, 2)}缩小变量作用域
如果有可能 , 尽量缩小变量作用范围 。除非它与减少嵌套的规则冲突 。
Bad
err := ioutil.WriteFile(name, data, 0644)if err != nil { return err}vs.
Good
if err := ioutil.WriteFile(name, data, 0644); err != nil { return err}如果需要在if之外使用函数调用的结果 , 则不应尝试缩小范围 。
Bad
if data, err := ioutil.ReadFile(name); err == nil { err = cfg.Decode(data) if err != nil { return err } fmt.Println(cfg) return nil} else { return err}vs.
Good
data, err := ioutil.ReadFile(name)if err != nil { return err}if err := cfg.Decode(data); err != nil { return err}fmt.Println(cfg)return nil避免裸参数
函数调用中的裸参数可能会损害可读性 。当参数名称的含义不明显时 , 请为参数添加C样式注释(/* … */) 。
Bad
// func printInfo(name string, isLocal, done bool)printInfo("foo", true, true)vs.
Good
// func printInfo(name string, isLocal, done bool)printInfo("foo", true /* isLocal */, true /* done */)更好的作法是 , 将裸bool类型替换为自定义类型 , 以获得更易读和类型安全的代码 。将来 , 该参数不仅允许两个状态(true/false) 。
type Region intconst ( UnknownRegion Region = iota Local)type Status intconst ( StatusReady = iota + 1 StatusDone // Maybe we will have a StatusInProgress in the future.)func printInfo(name string, region Region, status Status)使用原始字符串字面值 , 避免转义
Go支持原始字符串字面值 , 可以跨越多行并包含引号 。使用这些字符串可以避免更难阅读的手工转义的字符串 。
Bad
wantError := "unknown name:"test""vs.
Good
wantError := `unknown error:"test"`初始化结构体引用
在初始化结构引用时 , 请使用&T{}代替new(T) , 以使其与结构体初始化一致 。
Bad
sval := T{Name: "foo"}// 不一致sptr := new(T)sptr.Name = "bar"vs.
Good
sval := T{Name: "foo"}sptr := &T{Name: "bar"}格式化字符串放在Printf外部
如果你为Printf-style函数声明格式字符串 , 请将格式化字符串放在外面 , 并将其设置为const常量 。
这有助于go vet对格式字符串执行静态分析 。
Bad
msg := "unexpected values %v, %vn"fmt.Printf(msg, 1, 2)vs.
Good
const msg = "unexpected values %v, %vn"fmt.Printf(msg, 1, 2)命名Printf样式的函数
声明Printf-style函数时 , 请确保go vet可以检测到它并检查格式字符串 。
这意味着您应尽可能使用预定义的Printf-style函数名称 。go vet将默认检查这些 。有关更多信息 , 请参见Printf系列 。
如果不能使用预定义的名称 , 请以f结束选择的名称:Wrapf , 而不是Wrap 。go vet可以要求检查特定的Printf样式名称 , 但名称必须以f结尾 。
另请参阅"go vet:Printf家族检查" 。
五. 模式
测试表
在核心测试逻辑重复时 , 将表驱动测试与子测试一起使用 , 以避免重复代码 。
Bad
// func TestSplitHostPort(t *testing.T)host, port, err := net.SplitHostPort("192.0.2.0:8000")require.NoError(t, err)assert.Equal(t, "192.0.2.0", host)assert.Equal(t, "8000", port)host, port, err = net.SplitHostPort("192.0.2.0:http")require.NoError(t, err)assert.Equal(t, "192.0.2.0", host)assert.Equal(t, "http", port)host, port, err = net.SplitHostPort(":8000")require.NoError(t, err)assert.Equal(t, "", host)assert.Equal(t, "8000", port)host, port, err = net.SplitHostPort("1:8")require.NoError(t, err)assert.Equal(t, "1", host)assert.Equal(t, "8", port)vs.
Good
// func TestSplitHostPort(t *testing.T)tests := []struct{ give string wantHost string wantPort string}{ { give: "192.0.2.0:8000", wantHost: "192.0.2.0", wantPort: "8000", }, { give: "192.0.2.0:http", wantHost: "192.0.2.0", wantPort: "http", }, { give: ":8000", wantHost: "", wantPort: "8000", }, { give: "1:8", wantHost: "1", wantPort: "8", },}for _, tt := range tests { t.Run(tt.give, func(t *testing.T) { host, port, err := net.SplitHostPort(tt.give) require.NoError(t, err) assert.Equal(t, tt.wantHost, host) assert.Equal(t, tt.wantPort, port) })}


推荐阅读