跳转至

Golang编码规范之规范03

Golang编码规范之规范03

一 使用字段名初始化结构体

初始化结构体时,应该指定字段名称。现在由 go vet 强制执行。

  • Bad
k := User{"John", "Doe", true}
  • Good
k := User{
    FirstName: "John",
    LastName: "Doe",
    Admin: true,
}

例外:如果有 3 个或更少的字段,则可以在测试表中省略字段名称。

tests := []struct{
  op Operation
  want string
}{
  {Add, "add"},
  {Subtract, "subtract"},
}

二 本地变量声明

如果将变量明确设置为某个值,则应使用短变量声明形式 (:=)。

  • Bad
var s = "foo"
  • Good
s := "foo"

但是,在某些情况下,var 使用关键字时默认值会更清晰。例如,声明空切片

  • Bad
func f(list []int) {
  filtered := []int{}
  for _, v := range list {
    if v > 10 {
      filtered = append(filtered, v)
    }
  }
}
  • Good
func f(list []int) {
  var filtered []int
  for _, v := range list {
    if v > 10 {
      filtered = append(filtered, v)
    }
  }
}

三 nil 是一个有效的 slice

nil 是一个有效的长度为 0 的 slice,这意味着,

  1. 您不应明确返回长度为零的切片。应该返回nil 来代替。

  2. Bad

if x == "" {
  return []int{}
}
  • Good
if x == "" {
  return nil
}
  1. 要检查切片是否为空,请始终使用len(s) == 0。而非 nil

  2. Bad

func isEmpty(s []string) bool {
  return s == nil
}
  • Good
func isEmpty(s []string) bool {
  return len(s) == 0
}
  1. 零值切片(用var声明的切片)可立即使用,无需调用make()创建。

  2. Bad

nums := []int{}
// or, nums := make([]int)

if add1 {
  nums = append(nums, 1)
}

if add2 {
  nums = append(nums, 2)
}
  • Good
var nums []int

if add1 {
  nums = append(nums, 1)
}

if add2 {
  nums = append(nums, 2)
}

记住,虽然 nil 切片是有效的切片,但它不等于长度为 0 的切片(一个为 nil,另一个不是),并且在不同的情况下(例如序列化),这两个切片的处理方式可能不同。

四 缩小变量作用域

如果有可能,尽量缩小变量作用范围。除非它与 减少嵌套的规则冲突。

  • Bad
err := ioutil.WriteFile(name, data, 0644)
if err != nil {
 return err
}
  • 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
}
  • 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

五 避免参数语义不明确 (Avoid Naked Parameters)

函数调用中的意义不明确的参数可能会损害可读性。当参数名称的含义不明显时,请为参数添加 C 样式注释 (/* ... */)

  • Bad
// func printInfo(name string, isLocal, done bool)

printInfo("foo", true, true)
  • Good
// func printInfo(name string, isLocal, done bool)

printInfo("foo", true /* isLocal */, true /* done */)

对于上面的示例代码,还有一种更好的处理方式是将上面的 bool 类型换成自定义类型。将来,该参数可以支持不仅仅局限于两个状态(true/false)。

type Region int

const (
  UnknownRegion Region = iota
  Local
)

type Status int

const (
  StatusReady Status= 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\""
  • Good
wantError := `unknown error:"test"`