主题
04 · 切片、map 与错误处理心智模型
目标:把 Tour 里最容易「会写但讲不清」的三块钉死。
官方:Tour Basics · Effective Go · Data ·builtin·errors
1. Array vs Slice(必须能口头讲)
| Array | Slice | |
|---|---|---|
| 类型含长度 | yes,[3]int 与 [4]int 不同型 | []int |
| 传递 | 拷贝整块 | 拷贝「描述符」(指向同一底层数组) |
| 日常 | 较少直接用 | 默认用 slice |
直觉:slice ≈ { pointer, len, cap }。
go
a := []int{1, 2, 3}
b := a[:2] // 与 a 可能共享底层数组
b[0] = 9 // 可能影响 a[0]
c := append(a, 4) // 若 cap 不够会新分配;此后不一定共享1
2
3
4
2
3
4
AI 高发坑:对共享底层数组无感,改了子 slice 污染原数据。审查时问:「这里有没有无意共享?」
练习:自己实现「复制独立副本」——用 copy 或 append([]T(nil), s...),并对照 pkg.go.dev/builtin#copy。
2. Map
go
m := make(map[string]int)
m["a"] = 1
v, ok := m["b"] // 逗号 ok 判断键是否存在
delete(m, "a")1
2
3
4
2
3
4
要点:
- 零值
nilmap 可读(得零值),写会 panic → 先make或字面量。 - 遍历顺序不保证稳定(官方如此设计)。
- key 必须可比较;常见:string、数字、指针等。
对照 JS:更像 Map,不是随便当 Object 用;且无隐式字符串键转换那套。
3. 错误处理:Go 的「显式」文化
go
f, err := os.Open(name)
if err != nil {
return fmt.Errorf("open %s: %w", name, err)
}
defer f.Close()1
2
3
4
5
2
3
4
5
| 习惯 | 说明 |
|---|---|
多返回值最后常为 error | 先查 err 再使用其它返回值 |
fmt.Errorf + %w | 包装错误,保留可 errors.Is/As |
defer | 配对释放;了解 LIFO |
| 少用 panic | 留给真正「程序不能继续」;库代码优先返回 error |
官方阅读:
- https://go.dev/blog/error-handling-and-go
- https://pkg.go.dev/errors
- https://go.dev/doc/effective_go#errors
禁止肌肉记忆:data, _ := something() 除非你能说明为什么可以忽略。
4. 和 Node 的对照
| Node | Go |
|---|---|
throw / try/catch | 返回 error,调用方处理 |
async 错误进 Promise reject | 仍是 err;并发里别丢 err |
finally 关资源 | defer |
5. 最小练习(20 分钟)
写一个函数:func Freq(words []string) (map[string]int, error)
words == nil或空:返回空 map,err == nil(或按你定义的契约,写进测试)- 统计词频;不要修改入参 slice 的元素意外共享问题(本练习主要练 map)
下一篇会接接口;本篇先把 err 写顺。
6. AI 审查提示词
text
请按 Go 官方错误处理习惯审查:
1) 是否检查 err 2) 是否用 %w 包装 3) 是否有忽略 err
4) slice 是否存在危险的底层共享
不要新增依赖。指出具体行号。1
2
3
4
2
3
4
7. 验收清单
- [ ] 能讲清 slice 与 array 的区别
- [ ] 会用
v, ok := m[k] - [ ] 打开文件/解析类代码不再习惯性
_ = err - [ ] 知道
defer基本用途
