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 中添加的 bug 修复,我可以在我的 go.mod
文件中添加 require M v1.2.3
,以确保我的程序不会与 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 或更高版本中正确运行,因为它依赖于该补丁版本中修复的 bug,但我在代码中没有使用任何 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 1.21.1
的 go.mod
文件的模块中运行 go build
等 go
命令,则 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
和 toolchain
要求可以使用 go get
以与普通模块要求相同的方式进行更新。例如,如果您正在使用 Go 1.21 的候选版本之一,您可以通过运行以下命令在一个特定模块中开始使用 Go 1.21.0:
go get go@1.21.0
这将下载并运行 Go 1.21.0 来更新 go
行,并且将来调用 go
命令时将看到 go 1.21.0
行并自动重新调用该版本。
或者,如果您想在一个模块中使用 Go 1.21.0,但将 go
行保留为旧版本,以帮助维护与早期 Go 版本用户的兼容性,您可以更新 toolchain
行:
go get toolchain@go1.21.0
如果您想知道在特定模块中运行的 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 Toolchains”。
下一篇文章:使用 slog 进行结构化日志记录
上一篇文章:向后兼容性、Go 1.21 和 Go 2
博客索引