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 的一般標準,如下
所有程式碼都必須透過 golint 和 go vet 來檢查是否有問題。也最好將其設定在編輯工具中。
- Run
goimports -w .
on save - Run
golint ./...
andgo vet
to check for errors
Guidelines
Pointers to Interfaces
通常會包括兩個欄位:
- 指標類型資訊
- 資料指標,須以地址放入. 如果資料是一個記憶體位置則直接放入,如果是一個值則使用這個值的記憶體位置放入。
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 可以確保安全。
Bad | Good |
---|---|
|
|
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 函式時,使用 length
、capacity
參數,約提高 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 就別硬用
Bad | Good |
---|---|
|
|
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)