Go 语言习惯用法
将以下提示词粘贴到你的 AI 对话框中:
请根据 https://skillhub.cn/install/skillhub.md,安装 @user_3c6cb52e/golang-idioms-z8wk
技能介绍
为什么需要这份规范
Go 代码看起来简单,但团队代码里反复出现同类问题:错误被 log.Println 打印后就丢弃,调用方无法用 errors.Is 判断;包里定义了四五个方法的大接口,实际只用到其中一个;go func() 启动后没有任何停止路径,测试结束时 goroutine 泄漏;context.Context 被塞进 struct 里到处传。这些不是编译器能发现的错误,而是团队缺少一份可执行的约定。
这份技能提供什么
@user_3c6cb52e/golang-idioms-z8wk 把常见的 Go 习惯用法整理成规则清单,直接覆盖以下场景:
- 错误处理:统一用
fmt.Errorf("context: %w", err)包装并保留错误链;在合适的层级处理错误,不要一边log一边return;预期条件使用哨兵错误。 - 接口设计:接口在消费方定义,而不是实现方;优先组合小接口,直接复用标准库的
io.Reader、io.Writer、fmt.Stringer;单方法接口按方法名加-er命名。 - 并发模式:
goroutine必须通过context取消或关闭 channel 来停止;用sync.WaitGroup等待完成;生产与消费速度不匹配时使用带缓冲的 channel;worker pool 与 fan-out/fan-in 作为参考模式。 - context 传递:所有 I/O 函数把
context.Context作为第一个参数;外部调用设置context.WithTimeout或WithDeadline;创建后立即defer cancel();context.WithValue只用于 trace ID 这类请求级数据,不要存进 struct。 - 测试与模块管理:表驱动测试配合
t.Helper()与t.Cleanup();测试数据放入testdata/目录;每次提交前执行go mod tidy,锁定主版本并查看 changelog 后再升级。 - 零值设计:优先让 struct 的零值可以直接使用,而不是到处写构造函数;修改接收者用指针接收者,只读用值接收者;不应由用户直接设置的字段不要导出。
- 结构化日志:Go 1.21+ 直接使用标准库
log/slog,日志字段结构化,不做字符串拼接,每条日志带请求 ID、用户 ID 和操作名。 - 反模式清单:返回
interface{}代替具体类型、用init()做复杂初始化、用_忽略错误不加注释、共享状态优先用 mutex 而不是 channel、长函数里裸返回等。
适用边界
这套规则是通用工程约定,不替代具体业务的判断:context.WithValue 的使用需要克制,全局替换成 mutex 前要先确认锁竞争情况,表驱动测试也不是所有场景都更易读。规则会随 Go 版本演进,例如 log/slog 要求 Go 1.21+,升级依赖时应重新对照 changelog,而不是机械套用。
使用场景
- 审查团队 Go 服务的代码,统一错误包装与 context 传递写法,让 errors.Is 能正常工作
- 编写并发任务时设计 worker pool,并确保 goroutine 能通过 context 取消而不泄漏
- 重构接口定义,把大接口拆成小接口,并把接口声明移到消费方一侧
- 为 Go 1.21+ 项目接入 log/slog 结构化日志,替换字符串拼接并补齐请求 ID 字段
适合人员
- Go 服务端工程师:想让团队的错误处理和 context 传递风格保持一致
- 从 Java 或 Python 转 Go 的开发者:需要快速掌握 goroutine 与 channel 的惯用法
- 负责代码评审的技术负责人:希望有一份可对照的反模式清单来指出问题
- 维护开源 Go 库的作者:想让接口设计和测试写法符合社区预期