Uber 是一家美国硅谷的科技公司,也是 Go 语言的早期 adopter。其开源了很多 golang 项目,诸如被 Gopher 圈熟知的 zap、jaeger 等。2018 年年末 Uber 将内部的 Go 风格规范 开源到 GitHub,经过一年的积累和更新,该规范已经初具规模,并受到广大 Gopher 的关注,本文是对规范的整理
来源:https://github.com/xxjwxc/uber_go_guide_cn
1. 优先使用 strconv 而不是 fmt
下面代码是将int
转成字符串,使用两种方式进行性能对比。
1.1 反面示例
for i := 0; i < b.N; i++ { s := fmt.Sprint(rand.Int()) }
|
1.2 推荐用法
for i := 0; i < b.N; i++ { s := strconv.Itoa(rand.Int()) }
|
2. 避免字符串到字节的转换
不要反复从固定字符串创建字节 slice
。相反,应该只执行一次转换并保存结果到变量。
2.1 反面示例
for i := 0; i < b.N; i++ { w.Write([]byte("Hello world")) }
|
2.2 推荐用法
data := []byte("Hello world") for i := 0; i < b.N; i++ { w.Write(data) }
|
3. 指定map、slice容量
指定容量,能提升代码性能,特别是在追加切片时
3.1 反面示例
for n := 0; n < b.N; n++ { data := make([]int, 0) for k := 0; k < size; k++{ data = append(data, k) } }
|
3.2 推荐用法
for n := 0; n < b.N; n++ { data := make([]int, 0, size) for k := 0; k < size; k++{ data = append(data, k) } }
|
4.并发锁(Mutex
)
4.1 零值是有效的
零值 sync.Mutex
和 sync.RWMutex
是有效的。所以声明时不需要使用关键字new
。
Bad |
Good |
mu := new(sync.Mutex)
mu.Lock() |
var mu sync.Mutex
mu.Lock() |
4.2 在结构体中使用
如果使用结构体指针,mutex
应作为结构体的非指针字段。即使该结构体不被导出,也不能直接把 mutex
嵌入到结构体中。
1. 反面示例
type SMap struct { sync.Mutex data map[string]string }
func NewSMap() *SMap { return &SMap{ data: make(map[string]string), } }
func (m *SMap) Get(k string) string { m.Lock() defer m.Unlock()
return m.data[k] }
|
嵌入写法,会使Lock
和 Unlock
成为 SMap
导出方法,在外面可直接使用。
2. 推荐用法
type SMap struct { mu sync.Mutex data map[string]string }
func NewSMap() *SMap { return &SMap{ data: make(map[string]string), } }
func (m *SMap) Get(k string) string { m.mu.Lock() defer m.mu.Unlock()
return m.data[k] }
|
mutex 及其方法是 SMap
的实现细节,对其调用者不可见。
5. 使用sync/atomic
执行原子操作
使用 sync/atomic 包的原子操作对原始类型 (int32
, int64
等)进行操作,因为很容易忘记使用原子操作来读取或修改变量。
go.uber.org/atomic 通过隐藏基础类型为这些操作增加了类型安全性。此外,它包括一个方便的atomic.Bool
类型。
5.1 反面示例
type foo struct { running int32 }
func (f* foo) start() { if atomic.SwapInt32(&f.running, 1) == 1 { return } }
func (f *foo) isRunning() bool { return f.running == 1 }
|
5.2 推荐示例
type foo struct { running atomic.Bool }
func (f *foo) start() { if f.running.Swap(true) { return } }
func (f *foo) isRunning() bool { return f.running.Load() }
|