接口与 channel 的底层实现
需要预先了解: 接口 channel 内存分配与逃逸分析 接口与 channel 的底层实现 接口 里留下的 nil 陷阱、channel 里"收发是拷贝"的结论,都可以从运行时结构上得到解释。这一篇看 eface/iface/itab 和 hchan 的真实字段,以及一次发送、一次接收、一次关闭在底层分别发生了什么。 eface 与 iface 空接口和非空接口在运行时是两种结构: type eface struct { // 空接口 any / interface{} _type *_type // 动态类型 data unsafe.Pointer // 指向动态值的指针 } type iface struct { // 带方法的接口 tab *itab // 类型 + 方法表 data unsafe.Pointer // 指向动态值的指针 } 两者都是两个指针宽,在 64 位平台上是 16...
反射与 unsafe
需要预先了解: 接口 包与模块:代码组织 切片、map 与字符串的底层实现 反射与 unsafe 反射让程序在运行时检查类型、读取字段、调用方法;unsafe 则允许绕过类型系统直接操作内存。两者都是"不得已才用"的工具:反射带来灵活性和性能代价,unsafe 带来性能和不安全性。 为什么需要反射 编译期类型已知时,直接写代码就行。但有些场景类型只有在运行时才知道: encoding/json 要把任意 struct 转成 JSON,它不可能为每个类型写一份代码; ORM 要根据结构体字段生成 SQL; 配置加载要按 tag 把 YAML 映射到字段。 这些场景都需要"把类型信息当作数据来操作",这就是反射。 反射三定律 第一定律:接口值可以拆成反射对象。 var x any = User{Name: "一撮灰"} t := reflect.TypeOf(x) // 类型信息 v := reflect.ValueOf(x) // 值 第二定律:反射对象可以还原成接口值。 bac...
标准库(一):IO 与系统交互
需要预先了解: 接口 包与模块:代码组织 错误处理 标准库(一):IO 与系统交互 io.Reader 和 io.Writer 是 Go 标准库最重要的两个接口,几乎所有"数据从哪里来、到哪里去"的 API 都建立在这两个接口上。这一篇讲清楚它们的语义、如何用它们拼装数据流,以及 fmt、os、bufio、time、flag 的常用部分。 io.Reader 与 io.Writer 两个接口的签名极简: type Reader interface { Read(p []byte) (n int, err error) } type Writer interface { Write(p []byte) (n int, err error) } Read 的语义:最多读 len(p) 个字节到 p 里,返回实际读到的字节数 n。 n > 0:读到了数据,err 可能是 nil,也可能是 io.EOF(最后一个数据块同时到达末尾); n == 0, err == io.EOF:正常...
标准库(二):JSON 与 HTTP 服务
需要预先了解: 结构体与指针 标准库(一):IO 与系统交互 context 标准库(二):JSON 与 HTTP 服务 encoding/json 和 net/http 是写服务端程序的两块基石。这一篇从"结构体怎么变成 JSON"讲到"一个带中间件、超时和优雅关闭的迷你 REST 服务",把 反射与 unsafe 里看到的 tag 机制落到实际用法上。 encoding/json 基础 两个函数覆盖 90% 的场景: b, err := json.Marshal(v) // 值 -> JSON 字节切片 err = json.Unmarshal(data, &v) // JSON -> 值(必须传指针) u := User{Name: "一撮灰", Score: 98.5, Created: time.Date(2026, 9, 9, 12, 0, 0, 0, time.UTC)} b, _ := json.Marshal(u) fmt.Println...
测试
需要预先了解: 包与模块:代码组织 channel 标准库(二):JSON 与 HTTP 服务 测试 Go 把测试做成了语言工具链的一部分:_test.go 文件、go test 命令、testing 包,不需要额外框架。这一篇讲单元测试、表驱动测试、基准测试、模糊测试、示例测试和 HTTP 测试的写法,以及覆盖率怎么读。 测试文件的组织 测试代码放在以 _test.go 结尾的文件里,和被测试代码同一个目录: calc/ ├── calc.go // package calc ├── calc_test.go // package calc(同包测试) └── calc_ext_test.go // package calc_test(外部测试包) 两种包名的区别: 写法 能访问 适合 package calc 包内所有标识符(含未导出的) 测试内部实现 package calc_test 只有导出的标识符 测试对外 API,避免依赖...
性能分析与调优
需要预先了解: 内存分配与逃逸分析 垃圾回收 测试 性能分析与调优 性能优化的第一条原则是先测量,再优化。凭直觉猜"哪里慢"十有八九猜错,而且容易把代码改得更复杂却没有任何收益。Go 工具链把测量能力做得很完整:testing 做基准、pprof 找热点、trace 看调度。 方法论 建立基线:用 testing.B 写基准测试,记录当前的 ns/op、B/op、allocs/op; 找到瓶颈:用 pprof 看 CPU/内存/阻塞的分布,而不是猜; 只改一个变量:一次改一处,改完重新测量; 验证收益:与基线对比,没有明显提升就回退; 防止退化:把关键基准保留在仓库里,配合 CI 定期跑。 优化的收益通常遵循帕累托分布:少数几处热点贡献了大部分开销。先解决它们,再考虑细节。 采集 profile 文件式采集(runtime/pprof) 在程序里显式启动: f, _ := os.Create("cpu.prof") pprof.StartCPUProfile(f) defer pprof.StopCPUProfile() ...
Go 常见陷阱与最佳实践(全系列总结)
需要预先了解: 数组与切片 map 与字符串 函数 接口 错误处理 并发设计模式与陷阱总结 切片、map 与字符串的底层实现 接口与 channel 的底层实现 性能分析与调优 Go 常见陷阱与最佳实践(全系列总结) 这一篇不引入新知识,只做三件事:把散落在各篇的陷阱集中起来、把最佳实践整理成清单、给出一张全系列的知识地图。 陷阱大汇总 每一条都给出"现象 → 原因 → 出处",方便按需回查。 1. 切片共享底层数组 a := []int{1, 2, 3, 4, 5} b := a[:3] b = append(b, 100) // 容量够,直接写进 a[3] fmt.Println(a) // [1 2 3 100 5] 原因:切片的 array 指针指向同一块底层数组,容量足够时 append 就地写入。 出处:数组与切片、切片、map 与字符串的底层实现 2. range 拿到的是副本 points := []Point{...



