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 := 0
for i := 0; i < 10; i++ {
sum += i
}
- range
如果只需要第一項(key),就丟棄第二個:
for key := range m {
if key.expired() {
delete(m, key)
}
}
如果只需要第二項,則把第一項置為下劃線
sum := 0
for _, 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 main
import (
"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,不要用指針傳遞
//Map
package main
import (
"fmt"
)
type mp map[string]string
func (m mp) Set(k, v string) {
m[k] = v
}
func main() {
m := make(mp)
m.Set("k", "v")
fmt.Println(m)
}
//Channel
package main
import (
"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())
}
<pre><code><br /></code></pre>
如果需要對slice進行修改,通過返回值的方式重新賦值
//Slice
package main
import (
"fmt"
)
type slice []byte
func main() {
s := make(slice, 0)
s = s.addOne(42)
fmt.Println(s)
}
func (s slice) addOne(b byte) []byte {
return append(s, b)
}
<pre><code><br /></code></pre>
如果接收者是含有sync.Mutex或者類似同步字段的結構體,必須使用指針傳遞避免復制
package main
import (
"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()
}
<pre><code><br /></code></pre>
如果接收者是大的結構體或者數組,使用指針傳遞會更有效率。
package main
import (
"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())
}