這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
**注:此文檔參考官方指南Effective Golang和Golang Code Review Comments進行整理,力圖與官方及社區編碼風格保持一致。
gofmt
大部分的格式問題可以通過gofmt解決,gofmt自動格式化代碼,保證所有的go代碼一致的格式。
正常情況下,採用Sublime編寫go代碼時,外掛程式GoSublilme已經調用gofmt對代碼實現了格式化。
注釋
在編碼階段同步寫好變數、函數、包注釋,注釋可以通過godoc匯出產生文檔。
注釋必須是完整的句子,以需要注釋的內容作為開頭,句點作為結尾。
程式中每一個被匯出的(大寫的)名字,都應該有一個文檔注釋。
包注釋
每個程式包都應該有一個包注釋,一個位於package子句之前的塊注釋或行注釋。
包如果有多個go檔案,只需要出現在一個go檔案中即可。
//Package regexp implements a simple library //for regular expressions.package regexp
可匯出類型
第一條語句應該為一條概括語句,並且使用被聲明的名字作為開頭。
// Compile parses a regular expression and returns, if successful, a Regexp// object that can be used to match against text.func Compile(str string) (regexp *Regexp, err error) {
命名
使用短命名,長名字並不會自動使得事物更易讀,文檔注釋會比格外長的名字更有用。
包名
包名應該為小寫單詞,不要使用底線或者混合大小寫。
介面名
單個函數的介面名以"er"作為尾碼,如Reader,Writer
介面的實現則去掉“er”
type Reader interface { Read(p []byte) (n int, err error)}
兩個函數的介面名綜合兩個函數名
type WriteFlusher interface { Write([]byte) (int, error) Flush() error}
三個以上函數的介面名,類似於結構體名
type Car interface { Start([]byte) Stop() error Recover()}
混合大小寫
採用駝峰式命名
MixedCaps 大寫開頭,可匯出mixedCaps 小寫開頭,不可匯出
控制結構
if
if接受初始化語句,約定如下方式建立局部變數
if err := file.Chmod(0664); err != nil { return err}
for
採用短聲明建立局部變數
sum := 0for i := 0; i < 10; i++ { sum += i}
range
如果只需要第一項(key),就丟棄第二個:
for key := range m { if key.expired() { delete(m, key) }}
如果只需要第二項,則把第一項置為底線
sum := 0for _, value := range array { sum += value}
return
儘早return:一旦有錯誤發生,馬上返回
f, err := os.Open(name)if err != nil { return err}d, err := f.Stat()if err != nil { f.Close() return err}codeUsing(f, d)
函數
函數採用命名的多值返回
傳入變數和返回變數以小寫字母開頭
func nextInt(b []byte, pos int) (value, nextPos int) {
在godoc產生的文檔中,帶有傳回值的函式宣告更利於理解
錯誤處理
error作為函數的值返回,必須對error進行處理錯誤描述如果是英文必須為小寫,不需要標點結尾採用獨立的錯誤流進行處理
不要採用這種方式
if err != nil { // error handling} else { // normal code}
而要採用下面的方式
if err != nil { // error handling return // or continue, etc.}// normal code
如果傳回值需要初始化,則採用下面的方式
x, err := f()if err != nil { // error handling return}// use x
panic
盡量不要使用panic,除非你知道你在做什麼
import
對import的包進行分組管理,而且標準庫作為第一組
package mainimport ( "fmt" "hash/adler32" "os" "appengine/user" "appengine/foo" "code.google.com/p/x/y" "github.com/foo/bar")
goimports 實現了自動格式化
縮寫
採用全部大寫或者全部小寫來表示縮寫單詞
比如對於url這個單詞,不要使用UrlPony而要使用urlPony 或者URLPony`
參數傳遞
- 對於少量資料,不要傳遞指標
- 對於大量資料的struct可以考慮使用指標
- 傳入參數是map,slice,chan不要傳遞指標
- 因為map,slice,chan是參考型別,不需要傳遞指標的指標
接受者
名稱
統一採用單字母'p'而不是this,me或者self
type T struct{} func (p *T)Get(){}
類型
對於go初學者,接受者的類型如果不清楚,統一採用指標型
func (p *T)Get(){}
而不是
func (p T)Get(){}
在某些情況下,出於效能的考慮,或者類型本來就是參考型別,有一些特例
如果接收者是map,slice或者chan,不要用指標傳遞
//Mappackage mainimport ( "fmt")type mp map[string]stringfunc (m mp) Set(k, v string) { m[k] = v}func main() { m := make(mp) m.Set("k", "v") fmt.Println(m)}//Channelpackage mainimport ( "fmt")type ch chan interface{}func (c ch) Push(i interface{}) { c <- i}func (c ch) Pop() interface{} { return <-c}func main() { c := make(ch, 1) c.Push("i") fmt.Println(c.Pop())}
如果需要對slice進行修改,通過傳回值的方式重新賦值
//Slicepackage mainimport ( "fmt")type slice []bytefunc main() { s := make(slice, 0) s = s.addOne(42) fmt.Println(s)}func (s slice) addOne(b byte) []byte { return append(s, b)}
如果接收者是含有sync.Mutex或者類似同步欄位的結構體,必須使用指標傳遞避免複製
package mainimport ( "sync")type T struct { m sync.Mutex}func (t *T) lock() { t.m.Lock()}/*Wrong !!!func (t T) lock() { t.m.Lock()}*/func main() { t := new(T) t.lock()}
如果接收者是大的結構體或者數組,使用指標傳遞會更有效率。
package mainimport ( "fmt")type T struct { data [1024]byte}func (t *T) Get() byte { return t.data[0]}func main() { t := new(T) fmt.Println(t.Get())}**