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

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

大家好,我是煎鱼。

作为一个维护过许多有一定历史沉淀的 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 工程项目,要交付不同种类的可组合项目的程序员来说。相当于磨灭了一条道路。还是比较尴尬的。

责任编辑:武晓燕 来源: 脑子进煎鱼了
相关推荐

2024-03-12 09:13:28

Go语言main

2021-10-27 07:15:36

Go 循环引用

2021-11-08 11:02:01

Go函数重载

2023-02-26 23:36:08

PHPGo函数

2021-12-09 10:51:47

Go继承

2021-12-15 07:49:22

Go语言设计

2023-01-28 08:05:32

转换Go泛型

2024-01-01 08:10:40

Go语言map

2024-01-05 08:45:35

Go语言map

2024-05-28 08:55:52

2020-10-09 06:48:19

Pythonswitch语句

2020-07-22 08:01:41

Python开发运算符

2009-03-12 08:42:38

AndroidWMMTK

2021-08-02 09:31:20

Python工具代码

2021-06-11 00:03:31

鸿蒙智能手机

2021-02-01 13:53:53

StringlongJava

2023-04-03 11:21:29

PythonGoRust

2009-03-11 17:32:22

联发科WMAndroid

2021-07-13 08:09:34

微博推特评论

2021-01-22 15:31:47

JavaSwitchString
点赞
收藏

51CTO技术栈公众号