Go-advice 中文版本

Go-advice 中文版本

Go 箴言

Author: Rob Pike See more: https://go-proverbs.github.io/

Go 之禪

Author: Dave Cheney See more: https://the-zen-of-go.netlify.com/

代碼

使用 go fmt 格式化

讓團隊一起使用官方的 Go 格式工具,不要重新發明輪子。 嘗試減少代碼複雜度。 這將幫助所有人使代碼易於閱讀。

多個 if 語句可以摺疊成 switch

// NOT BAD
if foo() {
    // ...
} else if bar == baz {
    // ...
} else {
    // ...
}

// BETTER
switch {
case foo():
    // ...
case bar == baz:
    // ...
default:
    // ...
}

chan struct{} 來傳遞信號, chan bool 表達的不夠清楚

當你在結構中看到 chan bool 的定義時,有時不容易理解如何使用該值,例如:

type Service struct {
    deleteCh chan bool // what does this bool mean? 
}

但是我們可以將其改爲明確的 chan struct {} 來使其更清楚:我們不在乎值(它始終是 struct {}),我們關心可能發生的事件,例如:

type Service struct {
    deleteCh chan struct{} // ok, if event than delete something.
}

30 * time.Secondtime.Duration(30) * time.Second 更好

你不需要將無類型的常量包裝成類型,編譯器會找出來。
另外最好將常量移到第一位:

// BAD
delay := time.Second * 60 * 24 * 60

// VERY BAD
delay := 60 * time.Second * 60 * 24

// GOOD
delay := 24 * 60 * 60 * time.Second

time.Duration 代替 int64 + 變量名

// BAD
var delayMillis int64 = 15000

// GOOD
var delay time.Duration = 15 * time.Second

按類型分組 const 聲明,按邏輯和 / 或類型分組 var

// BAD
const (
    foo = 1
    bar = 2
    message = "warn message"
)

// MOSTLY BAD
const foo = 1
const bar = 2
const message = "warn message"

// GOOD
const (
    foo = 1
    bar = 2
)

const message = "warn message"

這個模式也適用於 var

  defer func() {
      err := ocp.Close()
      if err != nil {
          rerr = err
      }
  }()
  package main
  type Status = int
  type Format = int // remove `=` to have type safety
  
  const A Status = 1
  const B Format = 1
  
  func main() {
      println(A == B)
  }
*   `_ = f()``f()` 更好
*   `for _, c := range a[3:7] {...}``for i := 3; i < 7; i++ {...}` 更好
  func f(a int, _ string) {}
  func f(a int, b int, s string, p string)
  func f(a, b int, s, p string)
  var s []int
  fmt.Println(s, len(s), cap(s))
  if s == nil {
    fmt.Println("nil!")
  }
  // Output:
  // [] 0 0
  // nil!
  var a []string
  b := []string{}

  fmt.Println(reflect.DeepEqual(a, []string{}))
  fmt.Println(reflect.DeepEqual(b, []string{}))
  // Output:
  // false
  // true
  value := reflect.ValueOf(object)
  kind := value.Kind()
  if kind >= reflect.Chan && kind <= reflect.Slice {
    // ...
  }
  func f1() {
    var a, b struct{}
    print(&a, "\n", &b, "\n") // Prints same address
    fmt.Println(&a == &b)     // Comparison returns false
  }
      
  func f2() {
    var a, b struct{}
    fmt.Printf("%p\n%p\n", &a, &b) // Again, same address
    fmt.Println(&a == &b)          // ...but the comparison returns true
  }
*   例如: `errors.Wrap(err, "additional message to a given error")`
*   `for i := range a` and `for i, v := range &a` ,都不是 `a` 的副本
*   但是 `for i, v := range a` 裏面的就是 `a` 的副本
*   更多: [https://play.golang.org/p/4b181zkB1O](https://play.golang.org/p/4b181zkB1O)
*   `value := map["no_key"]` 將得到一個 0 值
*   `value, ok := map["no_key"]` 更好
*   而不是一個八進制參數 `os.MkdirAll(root, 0700)`
*   使用此類型的預定義常量 `os.FileMode`
*   [https://play.golang.org/p/mZZdMaI92cI](https://play.golang.org/p/mZZdMaI92cI)
    const (
      _ = iota
      testvar         // testvar 將是 int 類型
    )

vs

    type myType int
    const (
      _ myType = iota
      testvar         // testvar 將是 myType 類型
    )

不要在你不擁有的結構上使用 encoding/gob

在某些時候,結構可能會改變,而你可能會錯過這一點。因此,這可能會導致很難找到 bug。

不要依賴於計算順序,特別是在 return 語句中。

  // BAD
  return res, json.Unmarshal(b, &res)

  // GOOD
  err := json.Unmarshal(b, &res)
  return res, err

防止結構體字段用純值方式初始化,添加 _ struct {} 字段:

type Point struct {
  X, Y float64
  _    struct{} // to prevent unkeyed literals
}

對於 Point {X:1,Y:1} 都可以,但是對於 Point {1,1} 則會出現編譯錯誤:

./file.go:1:11: too few values in Point literal

當在你所有的結構體中添加了 _ struct{} 後,使用 go vet 命令進行檢查,(原來聲明的方式)就會提示沒有足夠的參數。

爲了防止結構比較,添加 func 類型的空字段

  type Point struct {
    _ [0]func() // unexported, zero-width non-comparable field
    X, Y float64
  }

http.HandlerFunchttp.Handler 更好

http.HandlerFunc 你僅需要一個 func,http.Handler 需要一個類型。

移動 defer 到頂部

這可以提高代碼可讀性並明確函數結束時調用了什麼。

JavaScript 解析整數爲浮點數並且你的 int64 可能溢出

json:"id,string" 代替

type Request struct {
  ID int64 `json:"id,string"`
}

併發

性能

  b := a[:0]
  for _, x := range a {
  	if f(x) {
	    b = append(b, x)
  	}
  }

爲了幫助編譯器刪除綁定檢查,請參見此模式 _ = b [7]

  res, _ := client.Do(req)
  io.Copy(ioutil.Discard, res.Body)
  defer res.Body.Close()
  ticker := time.NewTicker(1 * time.Second)
  defer ticker.Stop()
  func (entry Entry) MarshalJSON() ([]byte, error) {
	buffer := bytes.NewBufferString("{")
	first := true
	for key, value := range entry {
		jsonValue, err := json.Marshal(value)
		if err != nil {
			return nil, err
		}
		if !first {
			buffer.WriteString(",")
		}
		first = false
		buffer.WriteString(key + ":" + string(jsonValue))
	}
	buffer.WriteString("}")
	return buffer.Bytes(), nil
  }
*   瞭解更多: [https://github.com/golang/go/blob/master/src/sync/map.go#L12](https://github.com/golang/go/blob/master/src/sync/map.go#L12)
*   瞭解更多: [https://github.com/dominikh/go-tools/blob/master/cmd/staticcheck/docs/checks/SA6002](https://github.com/dominikh/go-tools/blob/master/cmd/staticcheck/docs/checks/SA6002)
*   來源: [https://go-review.googlesource.com/c/go/+/86976](https://go-review.googlesource.com/c/go/+/86976)
  // noescape hides a pointer from escape analysis.  noescape is
  // the identity function but escape analysis doesn't think the
  // output depends on the input. noescape is inlined and currently
  // compiles down to zero instructions.
  //go:nosplit
  func noescape(p unsafe.Pointer) unsafe.Pointer {
    x := uintptr(p)
    return unsafe.Pointer(x ^ 0)
  }
*   減少系統調用次數
*   重用 map 內存 (但是也要注意 m 的回收)
  for k := range m {
    delete(m, k)
  }
  m = make(map[int]int)

模塊

構建

測試

  func TestSomething(t *testing.T) {
    if testing.Short() {
      t.Skip("skipping test in short mode.")
    }
  }
  if runtime.GOARM == "arm" {
    t.Skip("this doesn't work under ARM")
  }

工具

*   `go list -f '{{ .Imports }}' package`
*   `go list -f '{{ .Deps }}' package`
*   [https://godoc.org/golang.org/x/perf/cmd/benchstat](https://godoc.org/golang.org/x/perf/cmd/benchstat)
*   來源:[https://twitter.com/bboreham/status/1105036740253937664](https://twitter.com/bboreham/status/1105036740253937664)

其他

  go func() {
    sigs := make(chan os.Signal, 1)
    signal.Notify(sigs, syscall.SIGQUIT)
    buf := make([]byte, 1<<20)
    for {
      <-sigs
      stacklen := runtime.Stack(buf, true)
      log.Printf("=== received SIGQUIT ===\n*** goroutine dump...\n%s\n*** end\n"  , buf[:stacklen])
    }
  }()
  var hits struct {
    sync.Mutex
    n int
  }
  hits.Lock()
  hits.n++
  hits.Unlock()
*   [https://godoc.org/net/http/httputil#DumpRequest](https://godoc.org/net/http/httputil#DumpRequest)
*   [https://golang.org/pkg/runtime/#Caller](https://golang.org/pkg/runtime/#Caller)
*   添加這一行代碼到 `bashrc`(或者其他類似的) `export CDPATH=$CDPATH:$GOPATH/src`
*   `[]string{"one", "two", "three"}[rand.Intn(3)]`
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://github.com/cristaloleg/go-advice/blob/master/README_ZH.md