大家好,我是建宇。中国新年快乐!年前我们在《醒醒吧,未来不会有 Go2 了!》一文中讨论了Go2的未来,明确指出未来是Go1.x.y主导的Go1时代。为了实现这个NorthStar目标,Go团队采取的策略是:增强Go1的向后兼容性(前文分享)和Go1的前向兼容性(本文重点)。这次提到的“向前兼容”指的是旧版本的Go编译新的Go代码。这个方向比较少谈,甚至是故意设计的。让我觉得,我还能这样做吗?Go1向前兼容Go团队的负责人RussCox。在以下几个方面进行了新的设计和调整。输出《Proposal: Extended forwards compatibility in Go[1]》已经讨论过,很可能会实现。版本号将保持在Go1.x.y。会涉及到的关键点如下:添加GOTOOLCHAIN环境变量的设置。改变了go行在工作模块中的解释方式,添加了新的工具链行来实现声明。这对应于go.mod文件的go行和工具链行。goget等命令的协同修改允许修改GOTOOLCHAIN和工作模块的go版本。增强工作模块的go.mod和工具链以声明Go版本号。我们在生成go模块的时候会在Go项目下生成一个go.mod文件。这将包含一个go行,该行将声明此模块应适用的go版本语义版本。如下图,声明go1.13:go.mod文件中的go版本声明本提案实施后,如果本地安装的Go工具链比go行声明的go版本更新,则直接提供所需的旧语义,而无需重新下载和调用旧版本的Go工具链。但是如果go行声明了更新的Go工具链,那么本地安装的Go工具链将下载并运行更新的工具链以满足其需要。下面是一个例子。在示例中,我们运行的是go1.30。但是在module中,有一个go.mod声明了go版本:go1.30.1Go1.30会下载并调用go1.30.1来完成命令,因为module中要求的go版本高于本地安装。但是如果go.mod文件声明:go1.20rc1Go1.30将自行提供go1.20rc1语义,而不是运行go1.20rc1工具链。因为本地安装的版本较新,旧语义的诉求可以通过GODEBUG来满足。声明Go工具链版本号可能会有同学想运行更新版本的Go工具链,但Go在语义上仍然使用旧版本。为了满足这个需求,go.mod文件也会支持toolchain行的设置,以支持使用新版本的toolchain。如果在go.mod文件中设置toolchain行,会指定使用的工具链版本,go行只指定语言语义的Go版本。go.mod文件如下:go1.18toolchaingo1.20rc1是为这个模块选择go1.18的语义,使用go1.20rc1工具链构建应用。增强的Go工具链GOTOOLCHAIN会在Go工具链中增加GOTOOLCHAIN环境变量的设置和使用,可以使用goenv-w来设置。gotest时也可以做如下调整:GOTOOLCHAIN=go1.17.2gotestgobuild编译时:GOTOOLCHAIN=go1.18rc1gobuild-omyprog.exe可能有同学会疑惑GOTOOLCHAIN的默认值从何而来,是什么值在那里?设置GOTOOLCHAIN=local:使用本地安装的Go工具链,不会下载不同版本的工具链。这是现在的默认行为。设置GOTOOLCHAIN=auto:使用在worker模块的go.mod中声明的go版本(当它比本地安装的Go工具链更新时)。GOTOOLCHAIN环境变量的默认值取决于Go工具链。标准的Go发行版默认为GOTOOLCHAIN=auto,它将控制权传递给go.mod文件。这是您在实施此提案后99%的时间会看到的默认行为。Gotoolchain的package里面也有很多东西,比如goget命令,也会改变go版本或者go.mod文件中的toolchain行来配合。小结在今天的文章中,我们介绍了Go1兼容性增强的“向前兼容”部分,其要点是:在go.mod文件和工具链GOTOOLCHAIN中增加go行和工具链行的使用。核心目的是暴露go的语法语义和go工具链的版本声明,实现隔离使用。再加上“向后兼容”中GODEBUG的使用,使得Go语言在做兼容的时候有了更多更大的使用空间来实现机制保障。这样一来,Go语言在这方面会变得异常复杂,理解成本也会增加。希望大家以后不要再踩这个坑了。
