idiomatic go

2017-01-12

Go的编码风格

在编码和重构时,go有些惯用方法,下面介绍一些惯用方法。

某些单词的拼写要保持一致性

正确方式


// marshaling
// unmarshaling
// canceling
// cancelation

错误方式

// marshalling
// unmarshalling
// cancelling
// cancellation

某些单词有多种拼写方式,Go有选择其中一个,并在项目编码中保持一致性。参考对比this vs this

句子间使用一个空格

正确方式

// Sentence one. Sentence two.

错误方式

// Sentence one.  Sentence two.

错误变量的命名

正确方式

// Package level exported error.
var ErrSomething = errors.New("something went wrong")

func main() {
	// Normally you call it just "err",
	result, err := doSomething()
	// and use err right away.

	// But if you want to give it a longer name, use "somethingError".
	var specificError error
	result, specificError = doSpecificThing()

	// ... use specificError later.
}

错误方式

var ErrorSomething = errors.New("something went wrong")
var SomethingErr = errors.New("something went wrong")
var SomethingError = errors.New("something went wrong")

func main() {
	var specificErr error
	result, specificErr = doSpecificThing()

	var errSpecific error
	result, errSpecific = doSpecificThing()

	var errorSpecific error
	result, errorSpecific = doSpecificThing()
}

品牌或单词开头大写,其他都小写

正确方式

// Exported.
var OAuthEnabled bool
var GitHubToken string

// Unexported.
var oauthEnabled bool
var githubToken string

错误方式

// Unexported.
var oAuthEnabled bool
var gitHubToken string

添加注释时在双划线后面添加一个空格

正确方式

// This is a comment
// for humans.

错误方式

//This is a comment
//for humans.

注意,注释是不添加空格的

//go:generate go run gen.go

使用defer时,必须保证所带来影响是可忽略的,或者可以增强可读性

若不能增强可读性,如函数只有1-2步就执行完了,那么就不要使用它。

显然,使用defer会带来性能的影响,特别是在秒级操作方法上。

defer的性能测试

包的名称使用单数形式

正确方式

golang.org/x/example/hello
golang.org/x/example/outyet
golang.org/x/mobile/example/basic
golang.org/x/mobile/example/flappy
github.com/shurcooL/tictactoe/player/bad
github.com/shurcooL/tictactoe/player/random

错误方式

golang.org/x/examples/hello
golang.org/x/examples/outyet
golang.org/x/mobile/examples/basic
golang.org/x/mobile/examples/flappy
github.com/shurcooL/tictactoe/players/bad
github.com/shurcooL/tictactoe/players/random

这是为了和go的命名风格保持一致

避免未使用的函数对象变量

正确方式

func (foo) method() {
	...
}

错误方式

func (f foo) method() {
	...
}

如果f未使用,那么可以清楚的知道,foo的方法和属性在method方法中未使用,增强了可读性。

空字符串校验

正确方式

if str == "" {
	...
}

错误方式

if len(str) == 0 {
	...
}

使用len来校验空字符串是正确的,但是len更适合在其他情况下使用。

第一种方式更具有可读性,可以清楚的字段str是字符串而不是数组

互斥锁保护罩

struct {
	...

	rateMu     sync.Mutex
	rateLimits [categories]Rate
	mostRecent rateLimitCategory
}

这里rateMu是一个互斥锁保护罩,它位于所保护的变量的顶部,好比一个帽子一样。所以无需对修改变量进行注释,和下面的效果是一样的。

struct {
	...

	// rateMu protects rateLimits and mostRecent.
	rateMu     sync.Mutex
	rateLimits [categories]Rate
	mostRecent rateLimitCategory
}

但新增无需rateMu保护的变量时,添加一个空行隔开

正确方式


 struct {
 	...

 	rateMu     sync.Mutex
 	rateLimits [categories]Rate
 	mostRecent rateLimitCategory
+
+	common service
 }
 

错误方式


 struct {
 	...

 	rateMu     sync.Mutex
 	rateLimits [categories]Rate
 	mostRecent rateLimitCategory
+	common     service
 }

不要在flag.Usage中使用os.Exit(2)

正确方式

 flag.Usage = func() {
	fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
	flag.PrintDefaults()
 }

错误方式

flag.Usage = func() {
	fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
	flag.PrintDefaults()
	os.Exit(2)
}

flag包的Usage在处理错误时,已经有对退出进行处理,所以无需再次调用os.Exit(2)

参考资料

https://dmitri.shuralyov.com/idiomatic-go