本文件列出在 Fuchsia 原始碼中編寫 Go 時必須遵循的慣例 樹。這些慣例是結合最佳做法 以及一些是為了維持一致性的選擇
本文所描述的慣例可視為新增或不常用的慣例 詳細做法請參閱 Effective Go 和 Go 程式碼審查留言。
一般
檔案中的宣告順序
Go 檔案中的宣告組織方式有何顯著差異,尤其是 通常會有一個包含完整內容的大型檔案,或是較小的檔案 這裡有一些經驗法則。
用於針對頂層宣告 (常數、介面、類型,以及 相關方法),通常會按功能群組列出,而且 以下順序:
- 常數 (包含列舉);
- 匯出的介面、類型和函式;
- 未匯出的介面、類型和函式,可支援匯出的類型。
例如,fidlgen lib 中的一組功能可能是 而另一個群組可能讀取並去標準化 JSON IR。這些 建議在規模擴大時成為個別檔案。
對於含方法的型別,依序列出:
- 類型宣告 (例如
type myImpl struct { ...
); - type assertion(s) (類型宣告) (如有);
- 方法 (先有匯出方法,第二是未匯出的方法);
- 在適用情況下,建議將實作介面的方法分組在一起,即所有
Foo
介面的方法,接著是Bar
介面的所有方法; - 通常,雖然已匯出,但
String()
方法仍會是最後一種。
如需列舉項目,請參閱如何定義列舉。
命名慣例
Go 相當著眼於如何命名。部分慣例:
var err error
var buf bytes.Buffer
var buf strings.Builder
var mu sync.Mutex
var wg sync.WaitGroup
var ctx context.Context
_, ok := someMap[someKey]
_, ok := someValue.(someType)
定義方法時,請確認接收器名稱一致 (當 。另請參閱指標與數值的比較。
避免匯出
在 Go 中,ID 可取消匯出或匯出 (公開/私密術語)
未使用)。匯出的聲明開頭為大寫字母
MyImportantThing
,未匯出的宣告開頭為小寫
字母 myLocalThing
。
請只匯出您確實需要且想重複使用的內容。注意事項 後來變更匯出是相對簡單的方法 直到必要為止讓說明文件更加簡潔。
如果您的類型採用反射方法,例如:範本中或自動建立 您可以將差異匯出到 go-cmp 欄位,在未匯出的 ID 中 只要使用來自這些領域的 小型資料集訓練即可再次提醒您,除了要匯出的欄位外, 其他規則。
名稱與類型相同的本機變數沒有問題。如果
您對匯出類型編寫 server := Server{...}
,然後 server :=
server{...}
適用於未匯出的類型。
一律不得使用已命名的傳回值
語意會造成混淆,而且容易出錯。即使在 演進過程中通常會導致 進而產生比實際情形更大的差異 變更。
系統會顯示建議, 特殊情況 或「已命名的回傳值適合這種特殊情況」我們的 規則,則不使用已命名的傳回值。
(定義介面時可以隨意命名。與 name 會在實作中傳回,但對語意沒有影響)。
定義列舉
定義列舉的標準模式如下:
type myEnumType int
const (
_ myEnumType = iota
myFirstEnumValue
mySecondEnumValue
)
只有在使用 iota
時才能省略類型。使用明確值時,您必須
重複類型:
const (
_ myEnumType = iota
myFirstEnumValue
mySecondEnumValue
// ...
myCombinedValue myEnumType = 100
)
此外,如果您希望這個列舉使用文字表示法:
var myEnumTypeStrings = map[myEnumType]string{
myFirstEnumValue: "myFirstEnumValue",
mySecondEnumValue: "mySecondEnumValue",
}
func (val myEnumType) String() string {
if fmt, ok := myEnumTypeStrings[val]; ok {
return fmt
}
return fmt.Sprintf("myEnumType(%d)", val)
}
例如 mdlint:
TokenKind.
偏好使用 map
,而非使用 switch
,因為這是很常見的做法
不斷更新程式碼,以便計算地圖以外的事物,例如:1 個FromString
建構函式運作效率低落,例如搜尋,或是預先計算 stringsToMyEnum
反向對應)。
您只能在為自然預設值的情況下使用值為 0
的列舉成員。
詳情請參閱列舉 FIDL API Rubric 項目。
另一個選項是使用 cmd/stringer
產生列舉。這會產生
程式碼的執行效率更高,但維護需要花費更多心力。別擔心!您可以使用
這個方法。
集合
如要表示集合,請使用對應空白的結構:
allMembers := map[uint32]struct{}{
5: {},
8: {},
}
將 Slice 執行個體化
如果您要使用常值定義切片,則 []T{ ... }
是
非常可靠
否則,請使用 var slice []T
建立空的配量,也就是不要使用。
slice:= []T{}
。
請注意,預先分配配量可帶來顯著的成效提升。 且可能比更簡單的語法來得好請參閱執行個體配量用途和 內部。
使地圖例項化
如要使用常值定義地圖,則 map[K]T{ ... }
是
非常可靠
否則,請使用 make(map[string]struct{})
建立空白地圖,這樣才不會。
使用 myEmptyMap := map[K]T{}
。
不正常退貨
當函式需要傳遞非正常的傳回時 (即「失敗」),請按照以下模式輸入幾個模式:
func doSomething(...) (data, bool)
,也就是傳回資料,否。func doSomething(...) *data
,亦即傳回資料,或 nil。func doSomething(...) (data, error)
,亦即傳回資料,或 錯誤。func doSomething(...) dataWrapper
,亦即傳回內含的包裝函式 作業結果的相關結構化資訊。
對於在各種口味下行走的時機,制定艱難且快速的規則。 但也有一些一般原則
在 Go 中用於多態性時,仰賴 nil
與現在很容易出錯
內容:nil
不是單位,其中有許多nil
,而各自不同
其他!因此,最好能夠傳回
使用的方法可能為多型時,額外的 ok
布林值
定義。建議只使用 nil
,而只顯示該方法的時機
只在單體式情境中使用或者換個方式說,使用圖案 (2)
比 (1) 更簡便,而且當所有呼叫端都使用
函式,不會透過介面鏡頭查看傳回的值。
傳回 error
表示發生問題,呼叫端
並附上相關解釋呼叫端可正常付款
以便讓錯誤逐漸增加,可能是在錯誤途中換行,或是進行某些
以及錯誤處理與復原與傳回 ok
方法的金鑰區別
布林值 (或 nil
資料),則是指在傳回 error
時,方法為
確保模型有足夠的瞭解背景資訊,進而分類為
錯誤。
舉例來說,LookupUser(user_id uint64)
傾向傳回 ok 布林值
表示找不到使用者,但 LookupCountryCode(code IsoCountryCode)
則較好
在特定國家/地區傳回 error
,指出設定錯誤。
無法查詢 (或要求的國家/地區無效)。
如果結果顯示 方法很複雜,需要使用結構化資料來描述。舉例來說, 適合使用資料包裝函式的驗證 API,這種 API 會 XML 文件並傳回錯誤路徑清單,每個路徑都含有警告或錯誤 。
靜態類型斷言
由於 Go 採用結構體型,例如型別是否實作介面 取決於其結構 (而非宣告)。我們可以輕鬆將 型別會實作一些介面,但實際上卻並非如此。這會產生 只到一陣子沒用到遠距離,導致編譯器難以辨識 發生錯誤。
為解決這個問題,請務必為所有介面編寫類型宣告 會實作 (是,標準程式庫中的類型) too):
var _ MyInterface = (*myImplementation)(nil)
這會建立型別 nil,其指派會強制編譯器檢查型別 確保一致性。
我們通常有許多導入都必須實作相同的介面, 例如:代表單節點實作的抽象語法樹狀結構 運算式介面在這些情況下,您應在 因此也記錄了所有預期的子類型
var _ = []MyInterface{
(*myImplementation)(nil),
(*myOtherImplementation)(nil),
...
}
關於放置這類斷言的位置,經驗法則如下: 如下:
- 最好在實作下方都採用單一型別斷言 獨立實作 (例如 這裡;
- 執行所有實作時,優先在介面下方使用分組類型的斷言
主要用途為搭配 (例如
Expression
的運算式節點) 代表 AST 的介面),例如 這裡。
嵌入
嵌入是 Go 中非常強大的概念。充分運用。已讀 嵌入相關說明。
嵌入介面或結構類型時,應先列出在 這些元素相應的介面或結構體類型,也就是說,嵌入的類型 會顯示為第一個欄位
指標與值的差異
如需方法接收器,請參閱指標與值一文 和接收器類型。 tl;dr 是希望保持一致,但如果有任何疑慮,請使用指標接收器。 對於特定型別,請務必保持一致的傳遞方式,例如 一律按照值傳遞、一律以參照方式傳遞 方法是由此型別定義 (含值或指標接收器)。
另請注意,根據值傳遞結構體,假設呼叫端 不正確或不正確。您可以輕鬆按住地圖、切片或 並因此進行修改所以在 Go 中,這個音不正確 也就是「以值傳遞是 const」。
如需具體建議,請參閱「實作介面」一節 有關方法接收器的資訊
實作介面
一般使用指標接收器實作介面;實作介面 使用值接收器會導致介面同時透過值和 指標,可使型別斷言變複雜,試圖列舉出可能性 介面的實作。查看執行個體 fxrev.dev/269371。
在某些情況下,使用值實作介面。 適用情況:
- 當類型從未用做指標時。舉例來說,自訂排序
方法是定義
type mySlice []myElement
並實作sort.Interface
(mySlice
)。 類型*mySlice
絕不會使用,因為[]myElement
已經是 參照。範例 請按這裡。 - 不應使用類型斷言或類型切換值
介面類型例如:
Stringer
通常會在以下位置實作: 值型別。接受val Stringer
的函式並不常見 開啟「val.(type)
」。
如有疑慮,請一律使用指標接收器實作介面。
留言
閱讀評論,特別是:
文件註解功能最適合完整的句子, 自動產生簡報第一句應為一句話的摘要 開頭為要宣告的名稱
// Compile parses a regular expression and returns, if successful,
// a Regexp that can be used to match against text.
func Compile(str string) (*Regexp, error) {
針對類型的文件註解
文章
「A」、「An」或「The」。Go 標準程式庫中的所有說明文件都會遵循這個
例如 // A Buffer is a
variable-sized ...
。
針對長度超過一句的註解,樣式通常會優先使用 一個摘要句子,然後加上空白行,然後再輸入一段內容 詳細資料。如需範例,請參閱 FileHeader 結構體。
換行時發生錯誤
使用 fmt.Errorf
傳播錯誤時:
- 使用
%s
即可只包含其字串值; - 使用
%w
允許呼叫端解除包裝並觀察包裝錯誤。附註:%w
這些包裝錯誤就會成為 API 的一部分
請參閱「在 Go 中處理錯誤 1.13,更具體來說是否 重點回顧
在某些情況下,必須採用錯誤傳播的方式
符合 API 合約規定,例如而不是 RDBMS 驅動程式庫
系統傳回的錯誤代碼,表示來電者可恢復通話。因此
而必須明確包裝潛在錯誤,而不是依賴
在 fmt.Errorf
。
fmt
個動詞
盡量避免使用 %v
,優先使用系統支援的特定 fmt 動詞
運算元這麼做的好處是可讓 go vet
檢查動詞
確實受到運算元支援。
如果運算元是未實作 fmt.Stringer
的結構,
%v
不太可能產生理想的結果;%+v
或%#v
可能會
更好的選擇
這項規則的常見例外狀況,是執行階段可能是 nil
的運算元 -
nil
值已由 %v
妥善處理,但並非所有其他動詞。
引用字串時,請使用 %q
,而不要明確呼叫 strconv.Quote
。
如要傳播錯誤,請參閱錯誤包裝一文。
GN 目標
Go 工具的一般 BUILD.gn
檔案如下所示:
go_library("gopkg") {
sources = [
"main.go",
"main_test.go",
]
}
go_binary("foo") {
library = ":gopkg"
}
go_test("foo_test") {
library = ":gopkg"
}
如果您擁有巢狀套件 (僅適用於
case),請使用
name = "go.fuchsia.dev/fuchsia/<path>/..."
表單以啟用
遞迴套件來源:
go_library("gopkg") {
name = "go.fuchsia.dev/fuchsia/tools/foo/..."
sources = [
"main.go",
"subdir/bar.go",
"extra/baz.go",
]
}
測試
結尾為 _test 的套件
通常 _test.go
檔案會與所測試程式碼位於相同的套件中 (例如
package foo
),而且可以存取未匯出的宣告。Go 也允許
您在套件後方加上
帶有 _test
的名稱 (例如 package foo_test
),此時會編譯為
獨立套件,但以主要二進位檔連結及執行。這種做法
稱為外部測試不要為套件命名
加上 _test
後置字串,除非您編寫外部測試,請改為參閱
測試套件。
在進行整合等級測試或與其互動時,優先使用外部測試 僅限測試中套件的匯出部分
使用結尾為 _test
的套件也對提供編譯過的範例也很有幫助
程式碼,即可透過套件選取器直接貼上。例如
範例
程式碼
和
來源。
測試公用程式
測試公用程式是套件中使用的輔助程式,可協助測試。是
「測試程式碼」該檔案位於具有 _test.go
後置字串的檔案中地點
測試公用程式;testutils_test.go
這個慣例是
,導致此程式碼不包含在非測試中
二進位檔,確保它不會在測試以外的地方使用。
測試套件
測試套件是一種程式庫,旨在簡化編寫測試的流程。這項服務
是「Production code」— 結尾不是 _test.go
字尾,但僅供
可使用這組 ID
測試套件的命名慣例是使用「test」/稱謂 指定要用於測試的套件名稱標準程式庫中的範例如下 httptest 和 Fuchsia 樹中的 iotest fidlgentest 和 emulatortest。
其他資源
- 有效的 Go
- 閱讀 Go 程式碼審查留言,並將此部分當做 Effective Go 的補充說明。
- Go 常見問題
- Golint
- Google:軟體服務語言設計 工程