Uber是世界领先的生活出行服务提供商 , 也是Go语言的早期adopter , 根据Uber工程博客的内容 , 大致可以判断出Go语言在Uber内部扮演了十分重要的角色 。Uber内部的Go语言工程实践也是硕果累累 , 有大量Go实现的内部工具被Uber开源到github上 , 诸如被Gopher圈熟知的zap、jaeger等 。2018年年末Uber将内部的Go风格规范开源到github , 经过一年的积累和更新 , 该规范已经初具规模 , 并受到广大Gopher的关注 。本文是该规范的中文版本 , 并”夹带“了部分笔者的点评 , 希望对国内Gopher有所帮助 。
注:该版本基于commit 3baa2bd翻译 , 后续不会持续更新 。
文章插图
一. 介绍样式(style)是支配我们代码的惯例 。术语“样式”有点用词不当 , 因为这些约定涵盖的范围不限于由gofmt替我们处理的源文件格式 。
本指南的目的是通过详细描述在Uber编写Go代码的注意事项来管理这种复杂性 。这些规则的存在是为了使代码库易于管理 , 同时仍然允许工程师更有效地使用Go语言功能 。
该指南最初由Prashant Varanasi和Simon Newton编写 , 目的是使一些同事能快速使用Go 。多年来 , 该指南已根据其他人的反馈进行了修改 。
本文档记录了我们在Uber遵循的Go代码中的惯用约定 。其中许多是Go的通用准则 , 而其他扩展准则依赖于下面外部的指南:
- Effective Go
- The Go common mistakes guide
- 保存时运行goimports
- 运行golint和go vet检查源码
二. 指导原则指向interface的指针
您几乎不需要指向接口类型的指针 。您应该将接口作为值进行传递 , 在这样的传递过程中 , 实质上传递的底层数据仍然可以是指针 。
接口实质上在底层用两个字段表示:
- 一个指向某些特定类型信息的指针 。您可以将其视为“类型” 。
- 数据指针 。如果存储的数据是指针 , 则直接存储 。如果存储的数据是一个值 , 则存储指向该值的指针 。
接收器(receiver)与接口
使用值接收器的方法既可以通过值调用 , 也可以通过指针调用 。
例如:
type S struct { data string}func (s S) Read() string { return s.data}func (s *S) Write(str string) { s.data = https://www.isolves.com/it/cxkf/yy/go/2019-10-14/str}sVals := map[int]S{1: {"A"}}// 你只能通过值调用ReadsVals[1].Read()// 下面无法通过编译:// sVals[1].Write("test")sPtrs := map[int]*S{1: {"A"}}// 通过指针既可以调用Read , 也可以调用Write方法sPtrs[1].Read()sPtrs[1].Write("test")同样 , 即使该方法具有值接收器 , 也可以通过指针来满足接口 。
type F interface { f()}type S1 struct{}func (s S1) f() {}type S2 struct{}func (s *S2) f() {}s1Val := S1{}s1Ptr := &S1{}s2Val := S2{}s2Ptr := &S2{}var i Fi = s1Vali = s1Ptri = s2Ptr// 下面代码无法通过编译 。因为s2Val是一个值 , 而S2的f方法中没有使用值接收器// i = s2Val《Effective Go》中有一段关于"pointers vs values"的精彩讲解 。
译注:关于Go类型的method集合的问题 , 在我之前的文章《关于Go , 你可能不注意的7件事》中有详尽说明 。零值Mutex是有效的
sync.Mutex和sync.RWMutex是有效的 。因此你几乎不需要一个指向mutex的指针 。
Bad:
mu := new(sync.Mutex)mu.Lock()vs.
Good:
var mu sync.Mutexmu.Lock()如果你使用结构体指针 , mutex可以非指针形式作为结构体的组成字段 , 或者更好的方式是直接嵌入到结构体中 。
如果是私有结构体类型或是要实现Mutex接口的类型 , 我们可以使用嵌入mutex的方法:
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]}
推荐阅读
- R语言完美重现STAMP结果图
- 天黑黑孙燕姿在线试听 孙燕姿天黑黑是什么语言
- C 语言实现的跨平台开发库 TBOX
- TypeScript编码指南
- js中对字符串进行base64编码和解码
- 家庭语言暴力危害不容忽视
- 在C语言中如何高效地复制和连接字符串?
- 「C语言」常用算法
- C语言有多少个关键字,你知道吗?
- 一文搞懂Python字符编码问题,值得收藏