Go 語言寫作習慣與命名規範
Go 的可見性不靠 public / private 關鍵字,而是由名稱首字母大小寫決定;命名因此既是風格問題,也是 API 設計的一部分。這篇整理日常撰寫 Go 時最常碰到的命名與寫作習慣,方便查閱與 code review 對照。
可見性:首字母決定匯出與否
| 首字母 | 可見性 | 範例 |
|---|---|---|
| 大寫 | 匯出(exported),套件外可存取 | Owner、NewServer |
| 小寫 | 未匯出(unexported),僅限套件內 | owner、parseConfig |
這條規則適用於型別、函式、方法、欄位、常數、變數等所有識別字。設計 API 時,先想清楚哪些符號要對外開放,再決定大小寫。
格式 化:交給 gofmt
Go 社群不靠人工爭論縮排,而是統一用 gofmt(或 go fmt ./...)自動格式化。括號、縮排、對齊等細節交給工具處理,團隊把精力放在命名與結構上即可。
# 格式化單檔
gofmt -w main.go
# 格式化整個模組
go fmt ./...
套件(Package)命名
- 全小寫、簡短、具語意:例如
bytes、tabwriter,不要用tabWriter、TabWriter或tab_writer。 - 避免底線:Go 命名原則上不用底線;例外見下方「底線的少數例外」。
- 目錄名 = 套件名:
src/encoding/base64匯入路徑是encoding/base64,套件名則是base64。 - 避免 util / common / helper:名稱太泛用,不利理解,也容易與其他套件撞名。
- 匯入別名只在衝突時使用:例如
import foob "path/to/foo_go_proto",且同一專案內盡量保持一致。
// Good:簡短、小寫
package usercount
// Bad:混合大小寫、底線、或過於泛用
package UserCount
package user_count
package util
套件名會成為存取前綴(如 bytes.Buffer),好的套件名能讓匯出符號不必重複套件語意:ring.New 比 ring.NewRing 更簡潔。
檔案命名
- 一般原始碼檔:全小寫,多字詞可用底線分隔(例如
http_client.go),也有專案習慣直接連寫。 - 測試檔:以
_test.go結尾,例如user_test.go。 - 僅供測試使用的套件:目錄或套件名可帶
_test後綴(如foo_test外部測試套件)。
變數與常數
MixedCaps,不用底線
多字詞名稱使用 MixedCaps(匯出)或 mixedCaps(未匯出),不要用 snake_case:
// Good
const MaxPacketSize = 512
var userCount int
// Bad
const MAX_PACKET_SIZE = 512
var user_count int
常數命名語意
常數名稱應描述代表的意義,而不是值的字面:
// Good
const MaxRetries = 3
// Bad:值本身沒有額外語意時不必定義常數
const Twelve = 12
變數長度與作用域
名稱長度大致與作用域大小成正比、與使用次數成反比:
- 小範圍(幾行內):
c、i、r(Reader)、w(Writer)可接受。 - 檔案或套件層級:用較完整的名稱,如
defaultTimeout。
單字名如 count、users 是好的起點;需要消歧時再加修飾,例如 userCount 與 projectCount。
縮寫詞(Initialisms)
URL、ID、HTTP、JSON 等縮寫在名稱中應整段同大小寫:
| 縮寫 | 匯出 | 未匯出 | 錯誤範例 |
|---|---|---|---|
| URL | URL | url | Url |
| ID | UserID | userID | UserId |
| HTTP | HTTPServer | httpClient | HttpServer |
| iOS | IOS(匯出時) | iOS | Ios |
函式與方法
Getter:不要加 Get 前綴
欄位 owner 的 getter 應叫 Owner(),不是 GetOwner():
owner := obj.Owner()
if owner != user {
obj.SetOwner(user)
}
若操作可能阻塞或涉及遠端呼叫,可用 Fetch、Compute 等動詞取代 Get,讓讀者預期成本。
建構函式
套件若只匯出一種主要型別,慣用 New;需要參數時用 NewWithName 等:
// package widget
func New() *Widget { ... }
func NewWithName(name string) *Widget { ... }
介面命名
單方法介面慣用方法名 + er 後綴:Reader、Writer、Formatter。若型別實作與標準介面語意相同的方法,應沿用相同名稱與簽名(例如 String() 而非 ToString())。
Receiver 命名
方法 receiver 名稱應短、一致、為型別縮寫:
| 不建議 | 建議 |
|---|---|
func (tray Tray) | func (t Tray) |
func (this *ReportWriter) | func (w *ReportWriter) |
func (self *Scanner) | func (s *Scanner) |
同一型別的所有方法應使用相同的 receiver 名稱。