Go 博客
Go 1.21 中的前向兼容性和工具链管理
除了 Go 1.21 对 向后兼容性的扩展承诺 之外,Go 1.21 还为 Go 代码引入了更好的前向兼容性,这意味着 Go 1.21 及更高版本将更好地注意不要错误编译需要更新版本的 Go 的代码。具体来说,go.mod
中的 go
行现在指定了所需的最低 Go 工具链版本,而在之前的版本中,它只是一个基本上没有强制执行的建议。
为了让您更轻松地跟上这些要求,Go 1.21 还引入了工具链管理,因此不同的模块可以使用不同的 Go 工具链,就像它们可以使用不同版本的必需模块一样。安装 Go 1.21 后,您将永远不必手动下载和安装 Go 工具链。go
命令可以为您完成此操作。
本文的其余部分将更详细地描述这两个 Go 1.21 更改。
前向兼容性
前向兼容性是指当 Go 工具链尝试构建针对更新版本的 Go 的 Go 代码时会发生什么。如果我的程序依赖于模块 M 并且需要在 M v1.2.3 中添加的错误修复,我可以将 require M v1.2.3
添加到我的 go.mod
中,从而保证我的程序不会针对旧版本的 M 进行编译。但是,如果我的程序需要特定版本的 Go,则一直没有办法表达这一点:特别地,go.mod
的 go
行没有表达这一点。
例如,如果我编写使用 Go 1.18 中添加的新泛型的代码,我可以在我的 go.mod
文件中编写 go 1.18
,但这不会阻止早期版本的 Go 尝试编译该代码,并产生以下错误:
$ cat go.mod
go 1.18
module example
$ go version
go version go1.17
$ go build
# example
./x.go:2:6: missing function body
./x.go:2:7: syntax error: unexpected [, expecting (
note: module requires Go 1.18
$
这两个编译器错误是误导性的噪音。真正的问题是 go
命令将其作为提示打印出来:程序编译失败,因此 go
命令指出了潜在的版本不匹配。
在这个例子中,我们很幸运构建失败了。如果我编写的代码只能在 Go 1.19 或更高版本中正确运行,因为它依赖于该补丁版本中修复的错误,但我没有在代码中使用任何 Go 1.19 特定的语言功能或包,则早期版本的 Go 将编译它并默默地成功。
从 Go 1.21 开始,Go 工具链将把 go.mod
中的 go
行视为规则,而不是准则,并且该行可以列出具体的点版本或候选版本。也就是说,Go 1.21.0 了解到它甚至无法构建在 go.mod
文件中说 go 1.21.1
的代码,更不用说那些说 go 1.22.0
等更晚版本的代码了。
我们允许旧版本的 Go 尝试编译较新代码的主要原因是避免不必要的构建失败。被告知您的 Go 版本太旧而无法构建程序非常令人沮丧,尤其是在它可能仍然可以工作的情况下(也许该要求过于保守),尤其是在更新到较新的 Go 版本是一件苦差事的时候。为了减少将 go
行强制为要求的影响,Go 1.21 还将工具链管理添加到核心发行版中。
工具链管理
当您需要 Go 模块的新版本时,go
命令会为您下载它。从 Go 1.21 开始,当您需要更新的 Go 工具链时,go
命令也会为您下载它。此功能类似于 Node 的 nvm
或 Rust 的 rustup
,但它内置于核心 go
命令中,而不是一个单独的工具。
如果您运行的是 Go 1.21.0 并且您运行了一个 go
命令(例如 go build
),在 go.mod
说 go 1.21.1
的模块中,Go 1.21.0 go
命令会注意到您需要 Go 1.21.1,下载它,并重新调用该版本的 go
命令以完成构建。当 go
命令下载并运行这些其他工具链时,它不会将它们安装到您的 PATH 中或覆盖当前安装。相反,它将它们下载为 Go 模块,继承了所有 模块的安全性和隐私优势,然后它从模块缓存中运行它们。
go.mod
中还有一个新的 toolchain
行,它指定在特定模块中工作时要使用的最低 Go 工具链。与 go
行不同,toolchain
不会对其他模块强加要求。例如,一个 go.mod
可能说:
module m
go 1.21.0
toolchain go1.21.4
这意味着要求 m
的其他模块需要提供至少 Go 1.21.0,但当我们在 m
本身中工作时,我们想要一个更新的工具链,至少是 Go 1.21.4。
可以使用 go get
更新 go
和 toolchain
要求,就像普通的模块要求一样。例如,如果您使用的是 Go 1.21 的某个候选版本,则可以通过运行以下命令开始在特定模块中使用 Go 1.21.0:
go get [email protected]
这将下载并运行 Go 1.21.0 来更新 go
行,并且 go
命令的未来调用将看到 go 1.21.0
行并自动重新调用该版本。
或者,如果您想在模块中开始使用 Go 1.21.0 但将 go
行保留为旧版本,以帮助保持与早期版本 Go 用户的兼容性,则可以更新 toolchain
行:
go get [email protected]
如果您想知道特定模块中正在运行哪个 Go 版本,答案与之前相同:运行 go version
。
您可以使用 GOTOOLCHAIN 环境变量强制使用特定版本的 Go 工具链。例如,要使用 Go 1.20.4 测试代码:
GOTOOLCHAIN=go1.20.4 go test
最后,GOTOOLCHAIN 设置采用 version+auto
的形式意味着默认使用 version
,但也允许升级到更新的版本。如果您安装了 Go 1.21.0,那么在发布 Go 1.21.1 后,您可以通过设置默认 GOTOOLCHAIN 来更改系统默认值:
go env -w GOTOOLCHAIN=go1.21.1+auto
您将永远不必手动下载和安装 Go 工具链。go
命令将为您完成此操作。
有关更多详细信息,请参阅“Go 工具链”。
下一篇文章: 使用 slog 进行结构化日志记录
上一篇文章: 向后兼容性、Go 1.21 和 Go 2
博客索引