Uber's Golang code style guide

學習 Uber 在 Golang 上的規範。 這裡做個筆記,主要還是去原文裡面看會比較詳細。

Golang 官方 style guide

Table of Contents

Uber 的 style guild 目錄列表中,看到 Uber style 的幾個大指標。

  • Introduction
  • Guidelines
  • Performance
  • Style
  • Patterns
  • Linting

Introduction

在 Uber 的規範中,大部分使用了 Go 的一般標準,如下

  1. Effective Go
  2. Go Common Mistakes
  3. Go Code Review Comments

所有程式碼都必須透過 golint 和 go vet 來檢查是否有問題。也最好將其設定在編輯工具中。

  • Run goimports -w . on save
  • Run golint ./... and go vet to check for errors

Guidelines

Pointers to Interfaces

通常會包括兩個欄位:

  1. 指標類型資訊
  2. 資料指標,須以地址放入. 如果資料是一個記憶體位置則直接放入,如果是一個值則使用這個值的記憶體位置放入。

Start Enums at One

在使用常數與 iota 時, 建議起始值為 1。 關於這點,我有碰過相關的坑,例如 gorm update 預設只會更新非零的值。

updating with struct it will only update non-zero fields by default

1type Operation int
2
3const (
4  Add Operation = iota + 1
5  Subtract
6  Multiply
7)

但是在特定情況下,0 具有代表意義的話,則使用 0。 例如:

1type LogOutput int
2
3const (
4  LogToStdout LogOutput = iota
5  LogToFile
6  LogToRemote
7)
8
9// LogToStdout=0, LogToFile=1, LogToRemote=2

Use "time" to handle time

時間會被假定錯誤,例如

  • A day has 24 hours
  • An hour has 60 minutes
  • A week has 7 days
  • A year has 365 days

以第一條為來說,加入 24 小時不一定會隔天。在做時間處理時使用 time package 可以確保安全。

BadGood
1func isActive(now, start, stop int) bool {
2  return start <= now && now < stop
3}
1func isActive(now, start, stop time.Time) bool {
2  return (start.Before(now) || start.Equal(now)) && now.Before(stop)
3}

Error Types

you should use a custom type, and implement the Error() method.

 1// package foo
 2
 3var ErrCouldNotOpen = errors.New("could not open")
 4
 5func Open() error {
 6  return ErrCouldNotOpen
 7}
 8
 9// package bar
10
11if err := foo.Open(); err != nil {
12  if errors.Is(err, foo.ErrCouldNotOpen) {
13    // handle
14  } else {
15    panic("unknown error")
16  }
17}

Avoid Mutable Globals

勁量避免在全域變數中定義可變的變數,例如 var _timeNow = time.Now

Avoid Using Built-In Names

不要使用容易令人混淆的關鍵字,例如 var error string

Performance

Prefer strconv over fmt

字串轉換使用 strconv 代替 fmt,約提高 55% 的效能。

Prefer Specifying Container Capacity

在使用 make 函式時,使用 lengthcapacity 參數,約提高 91% 的效能。

1make([]T, length, capacity)

Style

Group Similar Declarations

將同屬性相關的變數,用群組做分類例如:

 1import (
 2  "a"
 3  "b"
 4)
 5
 6const (
 7  a = 1
 8  b = 2
 9)
10
11var (
12  a = 1
13  b = 2
14)
15
16type (
17  Area float64
18  Volume float64
19)

如果變數屬不相關的,明確分開。

Import Group Ordering

There should be two import groups:

  • Standard library
  • Everything else
1import (
2  "fmt"
3  "os"
4
5  "go.uber.org/atomic"
6  "golang.org/x/sync/errgroup"
7)

Package Names

要命名 package 時,採用下面規範:

  • 全部小寫,也沒字母大寫跟底線
  • 名稱定義清楚,使引用 package 時不用重新命名
  • 名稱短而簡潔,但要有明確的識別度
  • 不使用負數,例如 net/url,而不是 net/urls
  • 別使用 "common", "util", "shared", or "lib"。這名稱沒有有用的資訊

See also Package Names and Style guideline for Go packages.

Function Grouping and Ordering

function 必須放置在全域定義 struct, const, var 之後。 並且做分群分類。

Reduce Nesting

if、for 減少槽狀式的寫法

Unnecessary Else

不用 else 就別硬用

BadGood
1var a int
2if b {
3  a = 100
4} else {
5  a = 10
6}
1a := 10
2if b {
3  a = 100
4}

Prefix Unexported Globals with _

在全域 var 和 const 變數加上前綴 _,在使用時可以明確的知道該變數是全域變數。 例外: 如果是 error 的變數,前綴應該使用 err

Use Raw String Literals to Avoid Escaping

1wantError := `unknown error:"test"`

Initializing Maps

Prefer make(..) for empty maps, and maps populated programmatically. This makes map initialization visually distinct from declaration, and it makes it easy to add size hints later if available.

1var (
2  // m1 is safe to read and write;
3  // m2 will panic on writes.
4  m1 = make(map[T1]T2)
5  m2 map[T1]T2
6)
comments powered by Disqus