规范
最后更新于:2022-04-02 02:35:46
[TOC]
## 规范
### sync.Mutex 无需设置指针
Bad
```
mu := new(sync.Mutex)
mu.Lock()
```
Good
```
var mu sync.Mutex
mu.Lock()
```
### 在边界处拷贝 Slices 和 Maps,禁止外部修改引用
#### 接收 Slices 和 Maps
当 map 或 slice 作为函数参数传入时,如果您存储了对它们的引用,则用户可以对其进行修改
Bad
```
func (d *Driver) SetTrips(trips []Trip) {
d.trips = trips
}
trips := ...
d1.SetTrips(trips)
// 你是要修改 d1.trips 吗?
trips[0] = ...
```
Good
```
func (d *Driver) SetTrips(trips []Trip) {
d.trips = make([]Trip, len(trips))
copy(d.trips, trips)
}
trips := ...
d1.SetTrips(trips)
// 这里我们修改 trips[0],但不会影响到 d1.trips
trips[0] = ...
```
#### 返回 slices 或 maps
同样,请注意用户对暴露内部状态的 map 或 slice 的修改。
Bad
```
type Stats struct {
mu sync.Mutex
counters map[string]int
}
// Snapshot 返回当前状态。
func (s *Stats) Snapshot() map[string]int {
s.mu.Lock()
defer s.mu.Unlock()
return s.counters
}
// snapshot 不再受互斥锁保护
// 因此对 snapshot 的任何访问都将受到数据竞争的影响
// 影响 stats.counters
snapshot := stats.Snapshot()
```
Good
```
type Stats struct {
mu sync.Mutex
counters map[string]int
}
func (s *Stats) Snapshot() map[string]int {
s.mu.Lock()
defer s.mu.Unlock()
result := make(map[string]int, len(s.counters))
for k, v := range s.counters {
result[k] = v
}
return result
}
// snapshot 现在是一个拷贝
snapshot := stats.Snapshot()
```
### Channel 的 size 要么是 1,要么是无缓冲的
channel 通常 size 应为 1 或是无缓冲的。默认情况下,channel 是无缓冲的,其 size 为零。任何其他尺寸都必须经过严格的审查。考虑如何确定大小,是什么阻止了 channel 在负载下被填满并阻止写入,以及发生这种情况时发生了什么。
### 枚举从 1 开始
Bad
```
type Operation int
const (
Add Operation = iota
Subtract
Multiply
)
// Add=0, Subtract=1, Multiply=2
```
Good
```
type Operation int
const (
Add Operation = iota + 1
Subtract
Multiply
)
// Add=1, Subtract=2, Multiply=3
```
在某些情况下,使用零值是有意义的(枚举从零开始),例如,当零值是理想的默认行为时。
```
type LogOutput int
const (
LogToStdout LogOutput = iota
LogToFile
LogToRemote
)
// LogToStdout=0, LogToFile=1, LogToRemote=2
```
### 错误类型
客户端需要检测错误,并且您已使用创建了一个简单的错误 errors.New,请使用一个错误变量
Bad
```
// package foo
func Open() error {
return errors.New("could not open")
}
// package bar
func use() {
if err := foo.Open(); err != nil {
if err.Error() == "could not open" {
// handle
} else {
panic("unknown error")
}
}
}
```
Good
```
// package foo
var ErrCouldNotOpen = errors.New("could not open")
func Open() error {
return ErrCouldNotOpen
}
// package bar
if err := foo.Open(); err != nil {
if err == foo.ErrCouldNotOpen {
// handle
} else {
panic("unknown error")
}
}
```
### 处理类型断言失败
Bad
```
t := i.(string)
```
Good
```
t, ok := i.(string)
if !ok {
// 优雅地处理错误
}
```
### 对于未导出的顶层常量和变量,使用_作为前缀
Bad
```
// foo.go
const (
defaultPort = 8080
defaultUser = "user"
)
// bar.go
func Bar() {
defaultPort := 9090
...
fmt.Println("Default port", defaultPort)
// We will not see a compile error if the first line of
// Bar() is deleted.
}
```
Good
```
// foo.go
const (
_defaultPort = 8080
_defaultUser = "user"
)
```
### 结构体中的嵌入
Bad
```
type Client struct {
version int
http.Client
}
```
Good
```
type Client struct {
http.Client
version int
}
```
### 本地变量声明
Bad
```
var s = "foo"
```
Good
```
s := "foo"
```
在某些情况下,var 使用关键字时默认值会更清晰。例如,声明空切片
Bad
```
func f(list []int) {
filtered := []int{}
for _, v := range list {
if v > 10 {
filtered = append(filtered, v)
}
}
}
```
Good
```
func f(list []int) {
var filtered []int
for _, v := range list {
if v > 10 {
filtered = append(filtered, v)
}
}
}
```
### nil 是一个有效的 slice
nil 是一个有效的长度为 0 的 slice
您不应明确返回长度为零的切片。应该返回nil 来代替
Bad
```
if x == "" {
return []int{}
}
```
Good
```
if x == "" {
return nil
}
```
要检查切片是否为空,请始终使用len(s) == 0。而非 nil
Bad
```
func isEmpty(s []string) bool {
return s == nil
}
```
Good
```
func isEmpty(s []string) bool {
return len(s) == 0
}
```
零值切片(用var声明的切片)可立即使用,无需调用make()创建
Bad
```
nums := []int{}
// or, nums := make([]int)
if add1 {
nums = append(nums, 1)
}
if add2 {
nums = append(nums, 2)
}
```
Good
```
var nums []int
if add1 {
nums = append(nums, 1)
}
if add2 {
nums = append(nums, 2)
}
```
### 小变量作用域
如果有可能,尽量缩小变量作用范围。除非它与 减少嵌套的规则冲突。
Bad
```
err := ioutil.WriteFile(name, data, 0644)
if err != nil {
return err
}
```
Good
```
if err := ioutil.WriteFile(name, data, 0644); err != nil {
return err
}
```
### 使用原始字符串字面值,避免转义
Bad
```
wantError := "unknown name:\"test\""
```
Good
```
wantError := `unknown error:"test"`
```
### 初始化 Struct 引用
在初始化结构引用时,请使用&T{}代替new(T),以使其与结构体初始化一致。
Bad
```
sval := T{Name: "foo"}
// inconsistent
sptr := new(T)
sptr.Name = "bar"
```
Good
```
sval := T{Name: "foo"}
sptr := &T{Name: "bar"}
```
### 使用 fmt.Errorf 代替 errors.New()
';