编码规范
代码格式
一定使用gofmt进行格式化。
注释
- 解释代码的作用 这种注释适合说明公共符号,比如注释解释向外提供的函数等,只有在函数功能简单而明显时才应该省略这些注释。
- 解释代码的实现 对代码中复杂、隐晦的逻辑进行说明。不要使用自然语言直接翻译代码,这不好。
- 解释代码实现的原因 注释可以解释代码的外部因素,这些因素脱离了上下文后通常难以理解。
- 解释代码可能出错的情况 注释应该提醒使用者一些潜在的限制条件或者无法处理的情况。
- 公共符号的注释 Google Style指南有两条规则:任何既不明显也不简短的公共功能必须予以注释;无论长度或者复杂度如何,库中的任何函数都必须予以注释。一个例外是,不要注释实现接口的方法!
命名规范
变量
- 简洁胜于冗长
- 缩略词使用全大写(如HTTP,XML等),但当其位于变量开头且不需要导出时,使用全小写(如使用ServeHTTP而不是ServeHttp, 使用XMLHTTPRequest或者xmlHTTPRequest)
- 全局变量在名字中需要携带更多的上下文信息,使得不同地方都可以轻易辨认出其含义。
函数
- 函数名不包含包名的上下文信息,因为包和函数总是成对出现的。
- 尽量简短
- 当名为foo的包中某个函数的返回类型是Foo时,可以省略类型信息而不产生歧义,当返回的是其余类型时,则可以在函数名中加入类型信息。
包
- 只有小写字母组成。
- 简短并包含一定的上下文信息
- 不和标准库同名。
- 尽量满足以下规则:
- 不使用常用变量名作为包名
- 使用单数而不是复数
- 谨慎地使用缩写,利用使用fmt在不破坏上下文的情况下比format更加简短。
控制流程
- 遵循线性原理,尽量避免嵌套,保持正常流程清晰。
- 尽量保持正常代码路径为最小缩进,优先处理特殊、错误情况,尽早开始重新循环来减少嵌套。
错误和异常处理
简单错误
简单错误是指金出现一次的错误,且在其他地方不需要捕获该错误,则优先使用error.New
来创建匿名变量直接表示简单错误;如果有格式化的要求,使用fmt.Errorf
对错误的Wrap和Unwrap
通过在fmt.Errorf
中使用%w
关键字可以将一个错误关联到一个错误链中,从而追踪错误发生的上下文环境。
错误判定
- 判定一个错误是否为特定错误,使用
errors.Is
;不同于==
,该方法将判定错误链上是否含有特定的错误。 errors.As
区别于Is的地方在于其将提取出制定类型的错误并将其赋值给定义好的变量。
Panic
若问题可以被屏蔽或解决,建议使用error代替panic;但当程序在启动阶段发生不可逆转的错误时,可以在init或main中使用panic崩溃程序。
性能优化
Benchmark
使用Go语言提供的基准性能测试工具benchmark
Slice
尽可能在使用make初始化切片时提供容量信息,减少内存分配次数。
关于Slice有可能会出现大内存未释放的情况。由于在已有的切片上创建新的切片,底层使用指针对数组进行引用,因而当在一个大切片上创建一个小切片时,大切片数组占据的内存不会被释放。一种解决办法是使用copy代替重切片。
Map
类似Slice,尽可能地在make时为Map提供容量信息,减少内存拷贝和重哈希的消耗。
字符串处理
go
1str += ...
2
3var builder strings.Builder
4builder.WriteString(str)
5
6buf := new(bytes.Buffer)
7buf.WriteString(str)
代码中展示了三种不同的字符串拼接操作,使用+的拼接性能最差,而Builder和Buffer相近,Buffer最快。由于字符串在go中是不可变类型,内存大小占用固定,因而使用+每次都会重新分配内存。而另外两个结构的底层都是[]byte
数组,通过内存扩容策略不需要每次都要重新分配内存。
空结构体
空结构体struct{}
不占用任何的内存空间,可以节省资源,或者仅仅作为占位符存在。
go
1s := make(map[int]struct{})
2
3s := make(map[int]bool)
代码中展示了两种set的书写方式,使用空结构体可以省去bool类型所占用的一个1个字节空间。。
atomic
锁的实现是通过OS来实现,属于系统调用,而atomic通过硬件实现,效率比锁高。