向前兼容

简单来说,就是老版本的编译器构建新的go版本写的代码。比如用Go1.20的编译器构建Go1.21的代码。

在Go1.21之前,老版本的编译器是否可以构建新版本Go的代码,取决于Go模块的代码是否使用了新版本Go的语言特性。

  • 例如,Go1.18才release的泛型特性,如果代码中使用了,那么go1.17的编译器就无法编译。

但是当前代码中如果没有使用go1.18的泛型特性,那么go1.17的编译器编译出来的程序的行为就和go1.18一样了吗?如果编译通过了,但是相同代码的结果却不一样,那么这就是向前兼容性的问题,而且更隐蔽,更难以发现。

  • 例如,Go1.18默认移除了使用[sha1-hash的x509证书的支持](https://tip.golang.org/doc/go1.18#sha1),虽然可以使用GODEBUG=x509sha1=1环境变量,保持默认行为和go1.18之前版本一致,但是默认行为其实是变更了。

为了提高向前兼容性,Go1.21现在会读取go.work或者go.modgo line(注意不是toolchain line)严格限制编译器Go版本的最低要求:

Go line就是go关键字后面的版本号,比如在go.mod中是这样的

module github.com/meirongdev/go121expl

go 1.21.0 // 该行就是go line

本地工具链为Go1.21之前版本

如果当前本地安装的Go版本是1.20,那么不受上面限制的约束。就算你项目的依赖中的Go lineGo1.21,只要没有使用Go1.21的新特性,那么构建很可能是可以成功的。但是GODEBUG的环境变量在两个版本之间控制的行为可能是不同的。还是有在运行时产生非预期结果的可能。

// go.mod
module github.com/meirongdev/go121expl/toolchain

go 1.20

require github.com/meirongdev/go121expl/toolchain/dep v0.1.1

replace github.com/meirongdev/go121expl/toolchain/dep => ./dep

本地工具链为Go1.21之后版本,且大于或等于构建模块及其依赖的最高go line版本

如果当前本地安装的Go版本是1.21.0或以上版本,当前项目的Go line1.20,依赖了go line1.21.0的模块,go build会提示

go: updates to go.mod needed; to update it:
        go mod tidy

go mod tidy执行后,main模块的 go line会更新为1.21.0

// go.mod
module github.com/meirongdev/go121expl/toolchain

go 1.21.0

require dep v0.1.1

replace dep => ./dep

如果当前工具链比所有模块的go line最高版本高,如go1.21.3,那么会增加一个toolchain line

// go.mod
module github.com/meirongdev/go121expl/toolchain

go 1.21.1

toolchain go1.21.3

require github.com/meirongdev/go121expl/toolchain/dep v0.1.1

replace github.com/meirongdev/go121expl/toolchain/dep => ./dep

本地工具链为Go1.21之后版本,但小于所构建模块及其依赖的最高go line版本

如果当前本地安装的Go版本是1.21.0或以上版本,当前项目的Go line1.20,依赖了go line1.21.1(及其以上版本的)的模块,会提示go: module ./dep requires go >= 1.21.1 (running go 1.21.0)。在go mod tidy执行之后,main模块的go line会更新为1.21.1,但是会增加一个toolchain line1.21.4

$ go mod tidy                     
go: module ./dep requires go >= 1.21.1; switching to go1.21.4
// go.mod
module github.com/meirongdev/go121expl/toolchain

go 1.21.1

toolchain go1.21.4

require github.com/meirongdev/go121expl/toolchain/dep v0.1.1

replace github.com/meirongdev/go121expl/toolchain/dep => ./dep

为什么toolchaingo1.21.4而不是go1.21.1呢?这是[Go toolchain switches](https://go.dev/doc/toolchain)机制决定的。

1.21是目前Go最新的release版本,toolchain会选择最新的的patch版本。

本地Go工具链版本 main模块 go line dep模块 go line go build
1.20 1.20 1.21.0 成功
1.21.0 1.20 1.21.0 go: updates to go.mod needed; to update it: go mod tidy
1.21.0 1.20 1.21.1 go: module ./dep requires go >= 1.21.1 (running go 1.21.0)
  • Go1.21这种变动的好处是
    • 这样让依赖于较新Go版本的修复的项目可以确保不会和老版本的Go工具链一起使用。
    • 让使用新Go特性的项目给予更好地错误报告:当需要一个新的go版本时,该问题可以清楚地显示,而不是尝试构建代码,并打印各种关于imports或者语法的问题。

参考