Quick Reference
| Topic | File |
|---|
| Concurrency patterns | INLINECODE0 |
| Interface and type system |
interfaces.md |
| Slices, maps, strings |
collections.md |
| Error handling patterns |
errors.md |
Goroutine Leaks
- - Goroutine blocked on channel with no sender = leak forever—always ensure channel closes or use context
- Unbuffered channel send blocks until receive—deadlock if receiver never comes
- INLINECODE4 on channel loops forever until channel closed—sender must INLINECODE5
- Context cancellation doesn't stop goroutine automatically—must check
ctx.Done() in loop - Leaked goroutines accumulate memory and never garbage collect
Channel Traps
- - Sending to nil channel blocks forever—receiving from nil also blocks forever
- Sending to closed channel panics—closing already closed channel panics
- Only sender should close channel—receiver closing causes sender panic
- Buffered channel full = send blocks—size buffer for expected load
- INLINECODE7 with multiple ready cases picks randomly—not first listed
Defer Traps
- - Defer arguments evaluated immediately, not when deferred function runs—
defer log(time.Now()) captures now - Defer in loop accumulates—defers stack, run at function end not iteration end
- Defer runs even on panic—good for cleanup, but recover only in deferred function
- Named return values modifiable in defer—
defer func() { err = wrap(err) }() works - Defer order is LIFO—last defer runs first
Interface Traps
- - Nil concrete value in interface is not nil interface—
var p *MyType; var i interface{} = p; i != nil is true - Type assertion on wrong type panics—use comma-ok: INLINECODE11
- Empty interface
any accepts anything but loses type safety—avoid when possible - Interface satisfaction is implicit—no compile error if method signature drifts
- Pointer receiver doesn't satisfy interface for value type—only
*T has the method
Error Handling
- - Errors are values, not exceptions—always check returned error
- INLINECODE14 after every call—unchecked errors are silent bugs
- INLINECODE15 for wrapped errors—
== doesn't work with INLINECODE17 - Sentinel errors should be
var ErrFoo = errors.New() not recreated - Panic for programmer errors only—return error for runtime failures
Slice Traps
- - Slice is reference to array—modifying slice modifies original
- Append may or may not reallocate—never assume capacity
- Slicing doesn't copy—
a[1:3] shares memory with INLINECODE20 - Nil slice and empty slice differ—
var s []int vs INLINECODE22 - INLINECODE23 copies min of lengths—doesn't extend destination
Map Traps
- - Reading from nil map returns zero value—writing to nil map panics
- Map iteration order is random—don't rely on order
- Maps not safe for concurrent access—use
sync.Map or mutex - Taking address of map element forbidden—
&m[key] doesn't compile - Delete from map during iteration is safe—but add may cause issues
String Traps
- - Strings are immutable byte slices—each modification creates new allocation
- INLINECODE26 over string iterates runes, not bytes—index jumps for multi-byte chars
- INLINECODE27 is bytes, not characters—use INLINECODE28
- String comparison is byte-wise—not Unicode normalized
- Substring shares memory with original—large string keeps memory alive
Struct and Memory
- - Struct fields padded for alignment—field order affects memory size
- Zero value is valid—
var wg sync.WaitGroup works, no constructor needed - Copying struct with mutex copies unlocked mutex—always pass pointer
- Embedding is not inheritance—promoted methods can be shadowed
- Exported fields start uppercase—lowercase fields invisible outside package
Build Traps
- -
go build caches aggressively—use -a flag to force rebuild - Unused imports fail compilation—use
_ import for side effects only - INLINECODE33 runs before main, order by dependency—not file order
- INLINECODE34 paths relative to source file—not working directory
- Cross-compile:
GOOS=linux GOARCH=amd64 go build—easy but test on target
快速参考
| 主题 | 文件 |
|---|
| 并发模式 | concurrency.md |
| 接口与类型系统 |
interfaces.md |
| 切片、映射、字符串 | collections.md |
| 错误处理模式 | errors.md |
Goroutine 泄漏
- - 在无发送者的通道上阻塞的 Goroutine 会永久泄漏——务必确保通道关闭或使用 context
- 无缓冲通道的发送操作会阻塞直到被接收——若接收者永不出现则导致死锁
- 通道上的 for range 会无限循环直到通道关闭——发送者必须调用 close(ch)
- Context 取消不会自动停止 Goroutine——必须在循环中检查 ctx.Done()
- 泄漏的 Goroutine 会累积内存且永远不会被垃圾回收
通道陷阱
- - 向 nil 通道发送会永久阻塞——从 nil 通道接收同样永久阻塞
- 向已关闭通道发送会引发 panic——关闭已关闭的通道也会 panic
- 只有发送者应关闭通道——接收者关闭会导致发送者 panic
- 缓冲通道满时发送会阻塞——根据预期负载设置缓冲区大小
- 多个 case 同时就绪时 select 随机选择——而非按列表顺序
Defer 陷阱
- - Defer 参数在声明时立即求值,而非 defer 函数执行时——defer log(time.Now()) 捕获的是当前时间
- 循环中的 defer 会累积——defer 按栈结构执行,在函数结束时而非迭代结束时运行
- 即使发生 panic,defer 也会执行——适合清理操作,但 recover 只能在 defer 函数中生效
- 可在 defer 中修改命名返回值——defer func() { err = wrap(err) }() 有效
- Defer 执行顺序为后进先出——最后声明的 defer 最先执行
接口陷阱
- - 接口中的 nil 具体值不等于 nil 接口——var p MyType; var i interface{} = p; i != nil 结果为 true
- 对错误类型进行类型断言会 panic——使用逗号 ok 模式:v, ok := i.(Type)
- 空接口 any 可接受任何类型但失去类型安全——应尽量避免使用
- 接口满足是隐式的——方法签名偏离时不会产生编译错误
- 指针接收者不满足值类型的接口——只有 T 拥有该方法
错误处理
- - 错误是值而非异常——务必检查返回的错误
- 每次调用后检查 err != nil——未检查的错误是静默缺陷
- 使用 errors.Is 检查包装错误——== 无法处理 fmt.Errorf(%w, err)
- 哨兵错误应定义为 var ErrFoo = errors.New() 而非重复创建
- 仅对程序员错误使用 panic——运行时失败应返回错误
切片陷阱
- - 切片是数组的引用——修改切片会修改原数组
- Append 可能重新分配也可能不分配——永远不要假设容量
- 切片操作不复制——a[1:3] 与 a 共享内存
- nil 切片与空切片不同——var s []int 与 s := []int{} 有区别
- copy() 复制两者长度的最小值——不会扩展目标切片
映射陷阱
- - 从 nil 映射读取返回零值——向 nil 映射写入会 panic
- 映射迭代顺序是随机的——不要依赖顺序
- 映射不支持并发安全访问——使用 sync.Map 或互斥锁
- 禁止取映射元素的地址——&m[key] 无法编译
- 迭代期间删除映射元素是安全的——但添加元素可能引发问题
字符串陷阱
- - 字符串是不可变的字节切片——每次修改都会创建新分配
- 对字符串使用 range 迭代的是符文而非字节——索引会跳过多字节字符
- len(s) 返回字节数而非字符数——使用 utf8.RuneCountInString()
- 字符串比较是基于字节的——而非 Unicode 规范化
- 子串与原字符串共享内存——大字符串会保持内存活跃
结构体与内存
- - 结构体字段会进行对齐填充——字段顺序影响内存大小
- 零值有效——var wg sync.WaitGroup 可直接使用,无需构造函数
- 复制包含互斥锁的结构体会复制未锁定状态的互斥锁——始终传递指针
- 嵌入不是继承——提升的方法可能被覆盖
- 导出字段以大写字母开头——小写字母字段在包外不可见
构建陷阱
- - go build 会积极缓存——使用 -a 标志强制重新构建
- 未使用的导入会导致编译失败——仅为了副作用时使用 _ 导入
- init() 在 main 之前执行,顺序由依赖关系决定——而非文件顺序
- go:embed 路径相对于源文件——而非工作目录
- 交叉编译:GOOS=linux GOARCH=amd64 go build——简单但需在目标平台测试