这是我参与「第三届青训营 -后端场」笔记创作活动的的第1篇笔记
Go 编码规范
风格
自动格式化代码工具:
- gofmt
- goimports
GoLand 内置了这些工具。
注释
注释内容:
- 代码作用
- 代码如何如何做的
- 代码实现的原因
- 代码什么情况下会出错(代码的限制条件)
公共符号始终要注释;
注释应该提供代码未表达出的上下文信息。
命名规范
降低阅读代码成本,重点考虑上下文信息,设计简介清晰的名称。
Good naming is like a good joke. If you have to explain it, it's not funny
—— Dave Cheney
variable
缩略词使用全大写 ServeHTTP 而非 ServerHttp。若不需要导出,则使用全小写。
变量距离被使用的地方越远,则命名中需要携带越多的上下文信息。`
// Good 使用"deadline"能够在变量名中携带更多信息量
func send(req *Request, deadline time.time)
// bad t的意义不明
func send(req *Reuqest, t time.time)
function
-
函数名不携带包名的上下文信息
-
函数名应该尽量简短
-
当名为 foo 的包的某个函数返回 Foo 类型时,可以在函数名省略其类型信息
package http // good func Serve(...) // bad func ServeHTTP(...)在调用时:
http.Serve(...) -
当 foo 包的某个函数返回类型 T 时,可以在函数名中加入类型信息
package time // bad func Parse(s String) (Duration, error) // good func ParseDuration(s string) (Duration, error)在调用时:
duration := time.ParseDuration(...)
package
必须满足:
- 只由小写字母组成
- 简短、包含一定的上下文信息
- 不要于标准库同名
尽量满足:
- 不是用常用变量名作为包名
- 使用单数而非复数
- 谨慎地使用缩写
控制流程
-
避免嵌套。
-
优先处理错误情况,尽早返回循环。若 if 有
return则省略else// bad if A { return } else { if B { return } else { if C { return } else { ... } } } // good if A { return } if B { return } if C { return } ...
错误与异常处理
-
简单的错误(仅出现一次,且其他地方不需要捕获)
errors.New("Simple Error")
-
复杂错误
// err 错误由其他代码抛出,在此处进行关联 补充错误上下文信息 fmt.Errorf("reading srcfiles list: %w", err) // 判断错误链是否有指定错误 errors.Is(err1, err2) // 取出特定错误 errors.As(err3, &err)
-
panic 程序遇到无法解决的异常
- 不建议在业务代码中使用
- 调用函数不包含
recorver会造成程序崩溃 - 若错误可以屏蔽或解决,建议使用
error - 当程序启动阶段发生不可逆转的错误时,可以在
init或main中使用 panic
-
recover
只能在
defer中使用defer func() { if e:= recover() && e != nil { err = fmt.Errorf("error: %v \n %s", err, debug.Stack()) } }
Go 性能优化
Benchmark
go test -bench=. -benchmem
技巧
slice
-
尽可能在初始化时提供容量信息
make([]int, capSize) -
使用
copy代替re-slice以解决大内存未释放的问题由于类似于
origin[1:100]这样的用法虽然创建了一个新的 Slice,但是还是复用着原 Slice 底层的数组,而如果原 Slice 不再使用的话,底层数组也会跟随新的 Slice 一直存在。使用copy函数可以避免此问题。copy(result, origin[1:100])
map
预分配内存
make(map[string]int, capSize)
减少扩容带来的 Rehash 和内存分配的耗时
拼接字符串
字符串在 Go 中时不可变类型,占用内存大小的固定的,每次 + 都会重新分配内存。strings.Builder 等底层时 []byte,并根据内存扩容策略,无需每次拼接都重新分配切片。
- 使用 + 性能最差
- strings.Builder, bytes.Buffer 相近,bytes.Buffer 转化为字符串时重新申请空间,而 strings.Builder 使用原有的空间
- strings.Buffer 最快
空结构体
空结构体不占据任何内存空间,可作为占位符使用。
atomic
通过硬件实现,效率比锁高
sync.Mutex 应用来保护一段逻辑而非只是一个变量
对于非数值操作,可以使用 atomic.Value,可以承载一个interface{}
测试工具
pprof
\