Go 为什么不支持从 main 包中导入函数?

2024年 3月 8日 80.0k 0

大家好,我是煎鱼。

作为一个维护过许多有一定历史沉淀的 Go 项目的人,在历史债务下和奇葩需求下,会遇到一些迫于业务需求的技术诉求。

诉求上是希望引用多项目,会出现从 main 包(package)中导入相关函数的这种使用诉求。为了将多 Go 工程合并到一个大单体中使用。

问题案例

具体的使用案例如下。

我们有一个 Go 应用,目录结构如下:

demo1
├── go.mod
├── main.go
└── x
    └── main.go

demo1/x/main.go 文件内代码如下:

package main

import (
 "fmt"
)

func main() {
 Main()
}

func Main() {
 fmt.Println("煎鱼进水了?")
}

demo1/main.go 文件内代码如下:

package main

import (
 "fmt"

 xmain "example.com/greet/x"  // 也就是本应用,上面的 x
)

func main() {
 fmt.Println("脑子进煎鱼了!")
 xmain.Main()
}

简单来讲,就是 demo1 这个 Go 项目,拥有两个 main 包。根目录下的 main.go 文件内引用了 x/main.go 内的 Main 方法。

运行该程序,看看运行结果:

$ go run main.go
main.go:6:2: import "example.com/greet/x" is a program, not an importable package

会直接报错,提示 x 包下是一个程序,而不是一个可导入的包。

为什么不支持导入 main 包

这个问题稍微可以收敛一下,关键内容是:为什么不支持导入 main 包内的函数?明明 main 包也是一个 package,其个别函数也是大写开头,是允许对外导出的。

我首先翻阅了一下 Go 语言规范(spec),确实没有非常明确禁止该项行为。但又确实在我们日常使用和编译运行时,会被拒绝运行。提示前面的错误。

随后又查看了具体的代码提交和 CL,实际上在 13 年前。现任 Go 核心团度负责人 @rsc 是提交过相应 main 包支持的。

如下 CL 所示:

图片图片

2011 年(13 年前)的 CL 移除了原本语言规范中定义的 “程序中的其他包都不能命名为 main” 的要求,也就是可以满足前文问题和背景中提到的使用诉求。

看到这里有的同学就疑惑了。怎么 13 年后的现在,2024 年。又不行了呢?而且感觉是不行好久了。

因为在 2015 年时,现任 Go 核心团队成员 @ianlancetaylor,又又又改了,增加了非常明确的判断,直接限制了。

如下代码变更:

图片图片

比较有趣的是,@rsc 和 @ianlancetaylor 的变更都是针对同一个 issues #4210:《cmd/go: go build does not reject importing commands》。

怎么后面又变了呢?@ianlancetaylor 给出的明确答复和定义:

图片图片

CL 4126053(原先 @rsc 提交的那次)是对描述语言规范的修改。该语言允许导入名为 main 的包。例如:在为使用 main 包的命令中的函数编写单元测试时,就可以使用它。

但这里的问题是关于 Go 工具,而不是语言。问题是 go 工具是否应该允许软件包导入定义命令的包。普遍的共识是不应该。

所提及的 Go 工具,覆盖的范围是:cmd/go。包含了 go build 等相关命令。因此是在受限制范围的。

经过如此切分场景,就能知道为什么语言规范上没有明确禁止。但 Go 工具上又明确拒绝了。因为其对应覆盖了不同的使用场景。

不支持的原因,结合讨论来看。

普遍认为支持 main 包的导入,会造成更大的复杂度和不安全性。

像是在 main 函数在编写时,通常会假定自己拥有完全的控制权,因此多个 main 包内的函数引入,可能会造成在 init 函数的初始化顺序、全局变量的注册等,都会产生程序上的冲突。

总结

在本次对 Go 工具限制从 main 包中导入相关函数的缘由,我们做了详尽的了解和分析。虽然 Go 官方这样的方式可以一刀切的解决复杂度和安全性的问题。

但有历史沉淀、债务的情况下,对于需要维护多个 Go 工程项目,要交付不同种类的可组合项目的程序员来说。相当于磨灭了一条道路。还是比较尴尬的。

相关文章

JavaScript2024新功能:Object.groupBy、正则表达式v标志
PHP trim 函数对多字节字符的使用和限制
新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
为React 19做准备:WordPress 6.6用户指南
如何删除WordPress中的所有评论

发布评论