用新工具链,却默认创建旧版本模块?开发者:这不太对吧
🎬 场景还原:一个让人困惑的瞬间
假设你刚升级到 Go 1.26,兴奋地想试试新特性:
# 1. 写个小程序
❯ cat main.go
package main
import "fmt"
func main() {
fmt.Println(new(42)) # Go 1.26 新语法
}
# 2. 直接运行,没问题!
❯ go run main.go
0x4a524480c0b0 ✅
# 3. 初始化模块
❯ go mod init myapp
go: creating new go.mod: module myapp
# 4. 查看生成的 go.mod
❯ cat go.mod
module myapp
go 1.25 # ❓ 等等,我用的是 1.26 啊?
# 5. 再编译,报错了!
❯ go build
./main.go:6:14: new(42) requires go1.26 or later
(-lang was set to go1.25; check go.mod) 🚫
开发者内心OS:我装的是 1.26,为啥默认给我配 1.25?
🔍 问题核心:默认行为变了
变更前(Go 1.25 及之前)
go mod init myapp
# 生成:go 1.25 (与工具链版本一致)✅
变更后(Go 1.26)
go mod init myapp
# 生成:go 1.25 (工具链版本 -1)❓
简单说:你用 1.26 的工具,go mod init 却默认给你创建 1.25 的模块。
🤷 为什么要这样改?
Go 团队在 #74748 中的解释:
"为了给
go directive提供一个更好的初始值"
官方逻辑:
- Go 政策:始终支持最近 2 个大版本
- 默认设为
当前版本-1,确保模块能在两个版本间兼容 - 方便企业用户在多版本环境中协作
但问题来了:这个"更好",对谁更好?🤔
😕 开发者的困惑点
1. 违背"最小惊讶原则"
用户预期:用 1.26 工具链 → 默认享受 1.26 特性
实际行为:用 1.26 工具链 → 默认限制在 1.25 特性
就像买了最新款手机,开机发现默认装的是上一代系统。
2. 增加了额外步骤
想用新特性?得手动改 go.mod:
module myapp
- go 1.25
+ go 1.26 # 手动升级
小项目:改一行无所谓
频繁创建模块:每次都要改,烦!
3. 理由不够充分
Issue 发起人直言:
"说这是'更好的初始值',但没解释为什么更好"
⚖️ 两派观点 PK
🔹 支持变更派(Go 团队视角)
| 理由 | 说明 |
|---|---|
| 🏢 企业友好 | 多版本团队中,默认保守版本更安全 |
| 🔙 兼容优先 | 确保模块能在两个支持版本中构建 |
| 🎯 可手动调整 | 需要新特性?改一行 go.mod 就行 |
🔹 反对变更派(开发者视角)
| 理由 | 说明 |
|---|---|
| 🧑💻 个人项目为主 | 大多数用户想用最新特性 |
| ⚡ 减少摩擦 | 默认行为应该"开箱即用" |
| 🔍 预期一致 | 工具链版本 = 默认语言版本,一直如此 |
🛠️ 临时解决方案
方案 1:手动指定版本
go mod init myapp -go=1.26
方案 2:初始化后快速修改
go mod init myapp
sed -i 's/go 1.25/go 1.26/' go.mod # Linux/Mac
# 或手动编辑 go.mod
方案 3:使用 go.work(多模块项目)
go work init
go work use .
# 在 go.work 中统一指定版本
🔮 可能的走向
目前这个 issue 处于讨论阶段,几种可能:
✅ 可能性 1:维持现状
- 企业场景确实有需求
- 个人用户可通过 -go 参数覆盖
✅ 可能性 2:折中方案
- 默认仍用工具链版本
- 增加 --compatible 参数创建保守版本
✅ 可能性 3: revert 回原行为
- 社区反馈强烈
- "最小惊讶"原则优先
💡 给开发者的建议
如果你刚遇到这个问题:
# 1. 初始化时直接指定版本
go mod init myapp -go=1.26
# 2. 或者在脚本中自动化
echo "go 1.26" >> go.mod # 追加覆盖
# 3. 关注 issue 进展
# https://github.com/golang/go/issues/77653
团队协作建议:
# 在 README 或 CONTRIBUTING 中明确:
项目要求:
Go 版本: >= 1.26
初始化命令: go mod init xxx -go=1.26