Skip to main content

Go 語言寫作習慣與命名規範

Go 的可見性不靠 public / private 關鍵字,而是由名稱首字母大小寫決定;命名因此既是風格問題,也是 API 設計的一部分。這篇整理日常撰寫 Go 時最常碰到的命名與寫作習慣,方便查閱與 code review 對照。


可見性:首字母決定匯出與否

首字母可見性範例
大寫匯出(exported),套件外可存取OwnerNewServer
小寫未匯出(unexported),僅限套件內ownerparseConfig

這條規則適用於型別、函式、方法、欄位、常數、變數等所有識別字。設計 API 時,先想清楚哪些符號要對外開放,再決定大小寫。


格式化:交給 gofmt

Go 社群不靠人工爭論縮排,而是統一用 gofmt(或 go fmt ./...)自動格式化。括號、縮排、對齊等細節交給工具處理,團隊把精力放在命名與結構上即可。

# 格式化單檔
gofmt -w main.go

# 格式化整個模組
go fmt ./...

套件(Package)命名

  • 全小寫、簡短、具語意:例如 bytestabwriter,不要用 tabWriterTabWritertab_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.Newring.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

變數長度與作用域

名稱長度大致與作用域大小成正比、與使用次數成反比:

  • 小範圍(幾行內):cir(Reader)、w(Writer)可接受。
  • 檔案或套件層級:用較完整的名稱,如 defaultTimeout

單字名如 countusers 是好的起點;需要消歧時再加修飾,例如 userCountprojectCount


縮寫詞(Initialisms)

URL、ID、HTTP、JSON 等縮寫在名稱中應整段同大小寫

縮寫匯出未匯出錯誤範例
URLURLurlUrl
IDUserIDuserIDUserId
HTTPHTTPServerhttpClientHttpServer
iOSIOS(匯出時)iOSIos

函式與方法

Getter:不要加 Get 前綴

欄位 owner 的 getter 應叫 Owner(),不是 GetOwner()

owner := obj.Owner()
if owner != user {
obj.SetOwner(user)
}

若操作可能阻塞或涉及遠端呼叫,可用 FetchCompute 等動詞取代 Get,讓讀者預期成本。

建構函式

套件若只匯出一種主要型別,慣用 New;需要參數時用 NewWithName 等:

// package widget
func New() *Widget { ... }
func NewWithName(name string) *Widget { ... }

介面命名

單方法介面慣用方法名 + er 後綴:ReaderWriterFormatter。若型別實作與標準介面語意相同的方法,應沿用相同名稱與簽名(例如 String() 而非 ToString())。


Receiver 命名

方法 receiver 名稱應短、一致、為型別縮寫

不建議建議
func (tray Tray)func (t Tray)
func (this *ReportWriter)func (w *ReportWriter)
func (self *Scanner)func (s *Scanner)

同一型別的所有方法應使用相同的 receiver 名稱。


避免冗餘命名

套件名 vs 匯出符號

不要重複套件已表達的語意:

冗餘較佳
widget.NewWidgetwidget.New
db.LoadFromDatabasedb.Load

變數名 vs 型別

編譯器與讀者通常能從型別推斷,不必在名稱裡再加型別:

冗餘較佳
var numUsers intvar users int
var nameString stringvar name string

同一作用域內若同時存在字串與解析後的值,可用 limitRaw / limit 區分。

外部上下文 vs 區域名稱

ads/targeting/revenue/reporting 套件裡,型別不必叫 AdsTargetingRevenueReportReport 即可;方法 (p *Project) Name()ProjectName() 更簡潔。


底線的少數例外

Go 命名原則上避免底線,僅在以下情況常見:

  1. 測試、benchmark、example 函式名(*_test.go 內)。
  2. 由程式碼產生器產生的套件名。
  3. 與 OS / cgo / syscall 互操作時,對應外部 API 的命名。

註解與文件

  • 匯出的型別、函式、套件應撰寫 godoc 註解;註解以識別字名稱開頭,說明用途而非實作細節。
  • 註解寫在宣告上方、中間無空行,會被視為該宣告的文件。
// Server 代表 HTTP 服務的執行個體。
type Server struct {
Addr string
}

寫作習慣速查

項目建議
可見性首字母大寫 = 匯出
多字詞MixedCaps / mixedCaps
套件名全小寫、簡短、語意清楚
Getter不用 Get 前綴
縮寫URL、ID 整段同大小寫
介面單方法常用 -er 後綴
Receiver1–2 字母、型別縮寫
格式化gofmt / go fmt
冗餘不重複套件名、型別名、上下文

參考資料