新手学Go应先写main函数、掌握go run/build区别、理解go mod tidy作用、善用pkg.go.dev查文档、正确使用interface{}与any、避免defer误用、配合WaitGroup与context控制goroutine。

从写第一个 main 函数开始,别碰框架
刚学 Go,最容易掉进的坑是:一上来就扒 gin 或 echo 源码、配 docker-compose.yml、搞 JWT 中间件。结果连 go run 和 go build 的区别都说不清。
真实路径是:用 go mod init 新建一个空模块 → 写死一个 main.go 打印 "hello" → 改成接收命令行参数(os.Args)→ 再加个本地文件读写(os.ReadFile)。这三步走完,你才真正摸到 Go 的执行模型和包管理逻辑。
-
go run编译并运行单文件,适合快速验证;go build生成可执行文件,才反映真实部署行为 - 别急着开
go.sum,但一定要理解go mod tidy是在补依赖树,不是“一键修复” - 所有标准库函数(比如
fmt.Println、strings.Split)优先查 pkg.go.dev,别信第三方博客的过时示例
接口不是 Java 那套,interface{} 和 any 别混用
Go 的接口是隐式实现,没 implements 关键字。新手常把 interface{} 当万能类型乱传,结果在 JSON 解析或 map 遍历时 panic。
Go 1.18 后 any 就是 interface{} 的别名,但语义不同:any 表示“我真不知道这是啥”,interface{} 在老代码里更常见。实际开发中,90% 的场景该用具体接口,而不是泛型兜底。
立即学习“go语言免费学习笔记(深入)”;
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 需要判断类型?用
switch v := x.(type),别写一堆if x != nil && reflect.TypeOf(x).Kind() == reflect.String - 函数参数要支持多种输入?先想能不能定义小接口,比如
type Reader interface { Read([]byte) (int, error) },比塞interface{}更安全 -
json.Unmarshal默认把数字转成float64,如果传的是interface{},取值前必须类型断言,否则 runtime panic
defer 不是 try-finally,它只管当前函数退出
看到 Python 的 with 或 Java 的 try-with-resources,就以为 defer 能自动关文件、回滚事务——这是最危险的误解。
defer 的时机是:当前函数 return 之前,按后进先出顺序执行。它不感知 goroutine 生命周期,也不跨函数调用链。常见翻车点是 defer 放在 for 循环里,或者 defer 调用闭包变量没捕获对。
- 打开文件后立刻 defer
f.Close(),但要检查f是否为nil,否则 panic - defer 后面跟函数调用(
defer close(ch)),不是函数定义(defer func(){...}()),后者容易误捕变量 - 数据库连接、HTTP client 这类资源,别靠 defer 管理,得用连接池或显式生命周期控制
并发不是加个 go 就完事,sync.WaitGroup 和 context.Context 必须一起用
写个 go doWork() 看起来很酷,但程序可能秒退,因为 main 函数跑完就结束了,goroutine 还没启动完。
真实服务里,goroutine 必须有退出信号。光靠 sync.WaitGroup 只能等完成,不能中断;光靠 context.Context 可以取消,但没人告诉它“等谁”。两者得配合。
- 启动 goroutine 前,先
wg.Add(1);结束前defer wg.Done();main 里wg.Wait() - 如果要超时或手动取消,用
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second),再把ctx传进 goroutine - 别在 goroutine 里直接用
log.Fatal,它会 kill 整个进程,应该返回 error 给主流程处理
error 是该 wrap 还是重造——这些没有标准答案,只能靠写真实小项目踩出来。

















