这是我参与「第五届青训营 」笔记创作活动的第4天
代码规范
代码格式化工具
gofmt官方自带的格式化工具
goimports在gofmt基础上增加了对依赖包的管理
注释的作用
- 解释代码作用
- 解释代码是如何实现的
- 解释代码为什么要实现
- 解释代码出错的原因
公共符号始终需要注释,注释应当提供代码没有的信息,代码本身需要有自解释性。
命名规范
-
var 变量名:
- 缩略词全大写,位于变量开头且不需要导出的时候使用全小写,例如:xmlHTTPRequest,ServeHTTP
- 变量距离使用的距离越远,变量的名字所带的信息就应该越多
-
func 函数:
- 函数名不携带包名的上下文信息,因为包名和函数名总是成对出现
- 函数名尽量简短
- 当名为foo的包返回某个类型Foo时,可以省略类型信息
- 名为foo的包返回类型T时,可以在函数名中添加类型信息
-
package 包:
- 只有小写字母组成,不包含大写字母和下划线
- 短小精悍
- 不和标准库同名
- 不使用常用变量名
- 用单数而不是复数
- 谨慎使用缩写
控制流程
- 避免嵌套
- 尽量保证正常代码缩进为最小缩进
错误和异常处理
-
简单错误
- 使用errors.New创建匿名变量来优先处理错误
- 如果有格式化需求,使用fmt.Errorf输出错误信息
-
错误的wrap和unwrap
- 错误可以行成一个链,使用fmt.Errorf,里面%w可以指定连接到哪一个错误上
- errors.unwrap可以把错误从错误链上取下来
-
错误判定
- errors.is不用于使用==,可以判定错误在错误链上是否存在该错误
- 在错误链上获取特定类型的错误使用errors.As
-
panic
- 不建议在业务代码中使用panic
- 调用函数不包括recover会导致程序崩溃
- 如果问题可以被解决建议使用error代替panic
- 特例:当程序启动阶段发生不可逆转的错误,可以在init或者main函数中panic
-
recover
- 只能在被defer的函数中使用
- 嵌套无法生效
- 只在当前goroutine生效
- def语句后进先出
性能测试
- benchmark
相关数据的含义:
slice优化
-
尽量指定make的长度,避免多次分配空间
-
slice原理:
常用的slice操作对应的代码,参考网站: Go Slice Tricks Cheat Sheet (ueokande.github.io)
//数据结构
type slice struct{
array unsafe.Pointer//数组指针
len int//长度
cap int//容量
}
切片尾接示意图:
- 通过切片截断,创建新的切片会复用之前使用的切片,内存不会释放
- 切片操作并不复制切片指向的元素
- 大切片新建小切片,内存未释放,示例:
测试结果如下:
因为创建的新切片会复用之前的切片,所以直接用切片操作内存不会释放,使用copy会释放之前的切片内存,内存占用减少
map优化
- 预分配空间,减少内存拷贝和rehash消耗的时间
字符串优化
-
三种字符串拼接方式:
+,string.builder,bytes.buffer.最快的是string.builder,内存占用最少的是bytes.buffer.性能最差的是+下图第一行是
+,第二行是string.builder,第三行是bytes.buffer
分析:
- 字符串在GO语言中是不可变类型,每次占用的内存大小是固定的
- 使用
+会重新分配内存 - string.builder和bytes.buffer底层是
[]bytes类型,采用内存扩容的策略,并不一定每次拼接都需要扩容操作.
builder 和 buffer的区别:
builder.string()直接将底层的[]byte类型转换成字符串类型返回,buffer.string()创建了一个新的内存空间存放字符串,分配了一次空间
使用builder.grow()方法可以提前分配[]byte的大小,buffer.grow()类似,提前分配可以减少运行时间,分配次数和内存消耗.
buffer.grow():
builder.grow():
buffer比builder多分配一次,因为buffer.string()分配了新的内存空间存放字符串:
空结构体
空结构体不占任何空间,可以当作占位符
atomic包
-
原子操作一个变量,可以使用atomic包,不用自己写一个mutex结构体加锁解锁;atomic包性能更好
-
如果需要保护一段逻辑代码,可以使用
sync.mutex.lock和sync.mutex.unlock;sync.mutex的加锁解锁是操作系统的系统调用,atomic操作是通过硬件实现的 -
对于非数值操作,可以使用atomic.value,可以承载一个interface{}