1.goroutine 泄漏
// 错误示例
go func() {
// 没有控制退出
for {
process()
}
}()
// 正确示例
done := make(chan bool)
go func() {
for {
select {
case <-done:
return
default:
process()
}
}
}()
2.channel 使用
// 常见错误:向已关闭的 channel 写入数据
close(ch)
ch <- data // panic
// 常见错误:重复关闭 channel
close(ch)
close(ch) // panic
3.切片容量问题
// 可能导致内存泄漏
data := []string{"a", "b", "c", "d", "e"}
slice := data[2:3] // slice 仍然引用着整个 data 底层数组
// 正确做法:使用 copy
newSlice := make([]string, 1)
copy(newSlice, data[2:3])
4.nil 切片 vs 空切片
var s1 []int // nil 切片
s2 := []int{} // 空切片
s3 := make([]int,0) // 空切片
// nil 切片和空切片的长度都是 0,但是 nil 切片的底层指针为 nil
5. 错误包装
// Go 1.13+ 推荐用法
if err != nil {
return fmt.Errorf("failed to process: %w", err)
}
6. 错误判断
// 判断具体错误
if errors.Is(err, os.ErrNotExist) {
// 处理文件不存在的情况
}
// 判断错误类型
var pathError *os.PathError
if errors.As(err, &pathError) {
fmt.Println(pathError.Path)
}
7. 接口实现检查
var _ io.Reader = (*MyType)(nil) // 编译时检查接口实现
8. 空接口 vs nil
var i interface{} = nil // nil 接口
var p *int = nil
var i interface{} = p // 非 nil 接口,包含 nil 指针
9. 并发访问
// 错误示例:并发读写 map
go func() { m["key"] = value }()
go func() { _ = m["key"] }()
// 正确示例:使用 sync.Map
var m sync.Map
m.Store("key", value)
value, ok := m.Load("key")
10. nil map
// 错误示例
var m map[string]int
m["key"] = 1 // panic
// 正确示例
m := make(map[string]int)
m["key"] = 1
11. 循环变量捕获
// 错误示例
for i := 0; i < 5; i++ {
go func() {
fmt.Println(i) // 所有 goroutine 可能打印相同的值
}()
}
// 正确示例
for i := 0; i < 5; i++ {
i := i // 创建新的变量
go func() {
fmt.Println(i)
}()
}
12. defer 相关
// defer 的参数在声明时就已经确定
i := 0
defer fmt.Println(i) // 将打印 0,而不是最终的值
i = 1
// 正确用法:使用闭包
defer func() { fmt.Println(i) }()
13. JSON 处理
// 常见错误:未导出字段无法序列化
type User struct {
name string // 小写,不会被序列化
Age int // 大写,会被序列化
}
// 正确示例
type User struct {
Name string `json:"name"`
Age int `json:"age"`
}