代码
- go fmt 你的代码
- 多个if语句可以转换成switch
- 使用
chan struct{}
来传送信号-
chan bool
明显更短,但是struct{}
更常见
-
- 使用
30 * time.Second
替代time.Duration(30) * time.Second
- 总是将 for-select 封装成一个函数
- 通过类型将
const
声明分组,通过逻辑和/或类型将var
分组 - 每一个块或IO函数调用应该是可被取消的或至少是可超时的
- 为整型 const 值实现
Stringer
接口 - 检查你的defer的错误
defer func(){ err := ocp.Close() if err != nil { rerr = err } }()
- 不要为panics或执行
os.Exit
使用checkErr
函数 - 不要为枚举使用别名,这将导致破坏类型安全
package main type Status = int type Format = int // remove `=` to have type safety const A Status = 1 const B Format = 1 func main() { println(A == B) }
- 如果你要省略返回参数,请明确省略
- 所以更偏向
_ = f()
而不是f()
- 所以更偏向
- 对于 slice 的初始化有一个更简短的样式
a := []T{}
命令行
- 在命令行上运行
go format
并比较diff- 这将确保所有的东西都是被生成的并且已承诺
- 为了运行最新版本Go的Travis-CI,使用
travis 1
- 在代码格式中检查是否有错误 `diff -u <(echo -n)< (gofmt -d .)
并发
- 在线程安全中使得某些代码被执行一次的最佳候选项是
sync.Once
- 不要使用 flags, mutexes, channels 或 atomics
- 永远阻塞使用
select{}
,省略channels,等待一个signal
性能
- 不要省略
defer
- 在大多数情况下,200ns的加速是可以忽略不计的
- 一直使用
defer r.Body.Close()
关闭 http body- 除非你需要泄漏go协程
- 不使用分配过滤
b := a[:0] for _, x := range a { if f(x) { b = append(b, x) } }
-
time.Time
有指针域time.Location
,这对go的GC是不友好的- 它与大数目的
time.Time
是相关的,使用时间戳替代
- 它与大数目的
- 使用
regexp.MustCompile
代替regexp.Compile
- 在很多情况下,你的正则表达式是不变的,所以在
func init
中初始化它
- 在很多情况下,你的正则表达式是不变的,所以在
- 在你的热门路径中,不要过度使用
fmt.Sprintf
。由于它要维护缓冲池和接口动态高度,因此代价高昂- 如果你正在使用
fmt.Sprintf("%s%s",var1,var2)
,考虑使用简单的字符串串联 - 如果你正在使用
fmt.Sprintf("%x",var)
,考虑使用hex.EncodeToString
或strconv.FormatInt(var,16)
- 如果你正在使用
- 如果你不使用 body,总是丢弃body,例如
io.Copy(ioutil.Discard, resp.Body)
- HTTP 客户端的传输将不会重用连接,除非消息体被读完并关闭
res, _ := client.Do(req) io.Copy(ioutil.Discard, res.Body) defer res.Body.Close()
- 不要在循环中使用 defer ,否则你将会获得很小的内存泄漏
- 因为 defer 将会没有任何原因的增加你的堆栈
- 不要忘记停止 ticker,除非你需要泄漏 channel
ticker := time.NewTicker(1 * time.Second) defer ticker.Stop()
构建
- 使用命令
go build -ldflags="-s -w"
为你的二进制文件减肥 - 简单的将你的测试分隔成不同的版本
- 使用
// +build integeration
,使用go test -v --tags integration
运行它们
- 使用
测试
-
go test -short
允许减少要运行的测试集func TestSomething(t *testing.T) { if testing.Short() { t.Skip("skipping test in short mode.") } }
- 依据架构跳过测试
if runtime.GOARM == "arm" { t.Skip("this doesn't work under ARM") }
- 测试使用名称
package_test
而不是package
- 对于快速基准比较,我们有一个
benchcmp
工具
工具
- 快速替换
go fmt -w -l -r "panic(err) -> log.Error(err)"
-
go list
允许查找所有直接和传递依赖go list -f '{{ .Imports }}' package
go list -f '{{ .Deps }}' package
其他
- 转储 go协程 https://stackoverflow.com/a/27398062/433041
go func() { sigs := make(chan os.Signal, 1) signal.Notify(sigs, syscall.SIGQUIT) buf := make([]byte, 1<<20) for { <-sigs stacklen := runtime.Stack(buf, true) log.Printf("=== received SIGQUIT ===\n*** goroutine dump...\n%s\n*** end\n", buf[:stacklen]) } }()
- 在编译期检查接口实现
var _ io.Reader = (*MyFastReader)(nil)
- 如果一个 len 的参数是 nil,则它是 0
- 匿名结构体很酷
var hits struct { sync.Mutex n int } hits.Lock() hits.n++ hits.Unlock()
-
httputil.DumpRequest
是一个非常有用的东东,不要创建你自己的