目录

Go 箴言

interface{}unsafepanic()

Go 之禅

  • 每个 package 实现单一的目的
  • 显式处理错误
  • 尽早返回,而不是使用深嵌套
  • 让调用者处理并发(带来的问题)
  • 在启动一个 goroutine 时,需要知道何时它会停止
  • 避免 package 级别的状态
  • 简单很重要
  • 编写测试以锁定 package API 的行为
  • 如果你觉得慢,先编写 benchmark 来证明
  • 适度是一种美德
  • 可维护性

代码

使用 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.Second 比 time.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
Stringerdefer
  defer func() {
      err := ocp.Close()
      if err != nil {
          rerr = err
      }
  }()
checkErrpanic()os.Exit()
  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()a := []T{}for _, c := range a[3:7] {...}for i := 3; i < 7; i++ {...}_
  func f(a int, _ string) {}
time.Beforetime.Aftertime.Subctxfunc foo(ctx Context, ...)
  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 {
    // ...
  }
%+vstruct{}
  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")rangefor i := range afor i, v := range &aafor i, v := range aavalue := map["no_key"]value, ok := map["no_key"]os.MkdirAll(root, 0700)os.FileModeiota
    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.HandlerFunchttp.Handler

移动 defer 到顶部

这可以提高代码可读性并明确函数结束时调用了什么。

JavaScript 解析整数为浮点数并且你的 int64 可能溢出

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

并发

sync.Onceselect{}math/randfunc NewSource(seed int64) SourcelockedSource

性能

deferdefer r.Body.Close()
  b := a[:0]
  for _, x := range a {
      if f(x) {
        b = append(b, x)
      }
  }
_ = b [7]
time.Timetime.Locationtime.Timeregexp.MustCompileregexp.Compilefunc initfmt.Sprintffmt.Sprintf("%s%s", var1, var2)fmt.Sprintf("%x", var)hex.EncodeToStringstrconv.FormatInt(var, 16)io.Copy(ioutil.Discard, resp.Body)
  res, _ := client.Do(req)
  io.Copy(ioutil.Discard, res.Body)
  defer res.Body.Close()
  • ** 不要在循环中使用 defer,否则会导致内存泄露

    • 因为这些 defer 会不断地填满你的栈(内存)
  • ** 不要忘记停止 ticker, 除非你需要泄露 channel

  ticker := time.NewTicker(1 * time.Second)
  defer ticker.Stop()
  • ** 用自定义的 marshaler 去加速 marshaler 过程

    • 但是在使用它之前要进行定制!
  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
  }
sync.Mapsync.Pool
  // 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)
  }
m := (*map[int]int)(atomic.LoadPointer(&ptr))
  for k := range m {
    delete(m, k)
  }
  • 分配新的
  m = make(map[int]int)

构建

go build -ldflags="-s -w" ...// +build integrationgo test -v --tags integration .CGO_ENABLED=0 go build -ldflags="-s -w" app.go && tar C app | docker import - myimage:latesttravis 1diff -u <(echo -n) <(gofmt -d .)

测试

package_testpackagego test -short
  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")
  }
testing.AllocsPerRungo test -test.bench=. -count=20

工具

gofmt -w -l -r "panic(err) -> log.Error(err)" .go listgo list -f '{{ .Imports }}' packagego list -f '{{ .Deps }}' packagebenchstatgo mod why -m go.modGOGC=off go build ...GODEBUGgo mod why -m go.mod

其他

  • ** dump goroutines 
  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 _ io.Reader = (*MyFastReader)(nil)
  var hits struct {
    sync.Mutex
    n int
  }
  hits.Lock()
  hits.n++
  hits.Unlock()
httputil.DumpRequestruntime.Callermap[string]interface{}{}CDPATHcd github.com/golang/gobashrcexport CDPATH=$CDPATH:$GOPATH/src[]string{"one", "two", "three"}[rand.Intn(3)]

总结

您可能感兴趣的文章: