使用 go fmt 格式化
让团队一起使用官方的 Go 格式工具,不要重新发明轮子。
尝试减少代码复杂度。 这将帮助所有人使代码易于阅读。
多个 if 语句可以折叠成 switch
// NOT BAD
if foo() {
// ...
} else if bar == baz {
// ...
} else {
// ...
}
// BETTER
switch {
case foo():
// ...
case bar == baz:
// ...
default:
// ...
}
复制代码
用 chan struct{} 来传递信号, chan bool 表达的不够清楚
当你在结构中看到 chan bool 的定义时,有时不容易理解如何使用该值,例如:
type Service struct {
deleteCh chan bool // what does this bool mean?
}
复制代码
但是我们可以将其改为明确的 chan struct {} 来使其更清楚:我们不在乎值(它始终是 struct {}),我们关心可能发生的事件,例如:
type Service struct {
deleteCh chan struct{} // ok, if event than delete something.
}
复制代码
30 * time.Second 比 time.Duration(30) * time.Second 更好
你不需要将无类型的常量包装成类型,编译器会找出来。
另外最好将常量移到第一位:
// BAD
delay := time.Second * 60 * 24 * 60
// VERY BAD
delay := 60 * time.Second * 60 * 24
// GOOD
delay := 24 * 60 * 60 * time.Second
复制代码
用 time.Duration 代替 int64 + 变量名
// BAD
var delayMillis int64 = 15000
// GOOD
var delay time.Duration = 15 * time.Second
复制代码
按类型分组 const 声明,按逻辑和/或类型分组 var
// BAD
const (
foo = 1
bar = 2
message = "warn message"
)
// MOSTLY BAD
const foo = 1
const bar = 2
const message = "warn message"
// GOOD
const (
foo = 1
bar = 2
)
const message = "warn message"
复制代码
这个模式也适用于 var。
- ** 每个阻塞或者 IO 函数操作应该是可取消的或者至少是可超时的
- ** 为整型常量值实现
Stringer接口 - ** 检查
defer中的错误
defer func() {
err := ocp.Close()
if err != nil {
rerr = err
}
}()
复制代码
- ** 不要在
checkErr函数中使用panic()或os.Exit() - ** 仅仅在很特殊情况下才使用 panic, 你必须要去处理 error
- ** 不要给枚举使用别名,因为这打破了类型安全
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()更好
-
**
我们用
a := []T{}来简单初始化 slice -
**
用 range 循环来进行数组或 slice 的迭代
for _, c := range a[3:7] {...}比for i := 3; i < 7; i++ {...}更好
-
**
多行字符串用反引号(`)
-
**
用
_来跳过不用的参数
func f(a int, _ string) {}
复制代码
- ** 如果你要比较时间戳,请使用
time.Before或time.After,不要使用time.Sub来获得 duration (持续时间),然后检查它的值。 - ** 带有上下文的函数第一个参数名为
ctx,形如:func foo(ctx Context, ...) - ** 几个相同类型的参数定义可以用简短的方式来进行
func f(a int, b int, s string, p string)
复制代码
func f(a, b int, s, p string)
复制代码
-
** 一个 slice 的零值是 nil
``` var s []int fmt.Println(s, len(s), cap(s)) if s == nil { fmt.Println("nil!") } // Output: // [] 0 0 // nil! ``` 复制代码
var a []string
b := []string{}
fmt.Println(reflect.DeepEqual(a, []string{}))
fmt.Println(reflect.DeepEqual(b, []string{}))
// Output:
// false
// true
复制代码
-
** 不要将枚举类型与
<,>,<=和>=进行比较- 使用确定的值,不要像下面这样做:
value := reflect.ValueOf(object)
kind := value.Kind()
if kind >= reflect.Chan && kind <= reflect.Slice {
// ...
}
复制代码
- ** 用
%+v来打印数据的比较全的信息 - ** 注意空结构
struct{}
func f1() {
var a, b struct{}
print(&a, "\n", &b, "\n") // Prints same address
fmt.Println(&a == &b) // Comparison returns false
}
func f2() {
var a, b struct{}
fmt.Printf("%p\n%p\n", &a, &b) // Again, same address
fmt.Println(&a == &b) // ...but the comparison returns true
}
复制代码
-
**
- 例如:
errors.Wrap(err, "additional message to a given error")
- 例如:
-
**
在 Go 里面要小心使用
range:for i := range aandfor i, v := range &a,都不是a的副本- 但是
for i, v := range a里面的就是a的副本
-
**
从 map 读取一个不存在的 key 将不会 panic
value := map["no_key"]将得到一个 0 值value, ok := map["no_key"]更好
-
**
不要使用原始参数进行文件操作
- 而不是一个八进制参数
os.MkdirAll(root, 0700) - 使用此类型的预定义常量
os.FileMode
- 而不是一个八进制参数
-
**
不要忘记为
iota指定一种类型
const (
_ = iota
testvar // testvar 将是 int 类型
)
复制代码
vs
type myType int
const (
_ myType = iota
testvar // testvar 将是 myType 类型
)
复制代码
不要在你不拥有的结构上使用 encoding/gob
在某些时候,结构可能会改变,而你可能会错过这一点。因此,这可能会导致很难找到 bug。
不要依赖于计算顺序,特别是在 return 语句中。
// BAD
return res, json.Unmarshal(b, &res)
// GOOD
err := json.Unmarshal(b, &res)
return res, err
复制代码
防止结构体字段用纯值方式初始化,添加 _ struct {} 字段:
type Point struct {
X, Y float64
_ struct{} // to prevent unkeyed literals
}
复制代码
对于 Point {X:1,Y:1} 都可以,但是对于 Point {1,1} 则会出现编译错误:
./file.go:1:11: too few values in Point literal
复制代码
当在你所有的结构体中添加了 _ struct{} 后,使用 go vet 命令进行检查,(原来声明的方式)就会提示没有足够的参数。
为了防止结构比较,添加 func 类型的空字段
type Point struct {
_ [0]func() // unexported, zero-width non-comparable field
X, Y float64
}
复制代码
http.HandlerFunc 比 http.Handler 更好
用 http.HandlerFunc 你仅需要一个 func,http.Handler 需要一个类型。
移动 defer 到顶部
这可以提高代码可读性并明确函数结束时调用了什么。