Uber Go语言编码规范( 五 )

函数分组与顺序

  • 函数应按粗略的调用顺序排序 。
  • 同一文件中的函数应按接收者分组 。
因此 , 导出的函数应先出现在文件中 , 放在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 intif b { a = 100} else { a = 10}vs.
Good
a := 10if 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类型对于未导出的顶层常量和变量 , 使用_作为前缀
译注:这个是Uber内部的惯用法 , 目前看并不普适 。
在未导出的顶级vars和consts ,  前面加上前缀_ , 以使它们在使用时明确表示它们是全局符号 。
例外:未导出的错误值 , 应以err开头 。
基本依据:顶级变量和常量具有包范围作用域 。使用通用名称可能很容易在其他文件中意外使用错误的值 。
Bad
// foo.goconst ( defaultPort = 8080 defaultUser = "user")// bar.gofunc Bar() { defaultPort := 9090 ... fmt.Println("Default port", defaultPort) // We will not see a compile error if the first line of // Bar() is deleted.}vs.
Good
// foo.goconst ( _defaultPort = 8080 _defaultUser = "user")结构体中的嵌入
嵌入式类型(例如mutex)应位于结构体内的字段列表的顶部 , 并且必须有一个空行将嵌入式字段与常规字段分隔开 。
Bad
type Client struct { version int http.Client}vs.
Good
type Client struct { http.Client version int}使用字段名初始化结构体
初始化结构体时 , 几乎始终应该指定字段名称 。现在由go vet强制执行 。
Bad
k := User{"John", "Doe", true}vs.
Good
k := User{ FirstName: "John", LastName: "Doe", Admin: true,}例外:如果有3个或更少的字段 , 则可以在测试表中省略字段名称 。
tests := []struct{}{ op Operation want string}{ {Add, "add"}, {Subtract, "subtract"},}本地变量声明
如果将变量明确设置为某个值 , 则应使用短变量声明形式(:=) 。
Bad
var s = "foo"vs.
Good
s := "foo"但是 , 在某些情况下 , var 使用关键字时默认值会更清晰 。例如 , 声明空切片 。
Bad
func f(list []int) { filtered := []int{} for _, v := range list { if v > 10 { filtered = append(filtered, v) } }}vs.
Good
func f(list []int) { var filtered []int for _, v := range list { if v > 10 { filtered = append(filtered, v) } }}nil是一个有效的slice
nil是一个有效的长度为0的slice , 这意味着: