脚本之家,脚本语言编程技术及教程分享平台!
分类导航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|shell|

服务器之家 - 脚本之家 - Golang - Go 为什么不支持从 main 包中导入函数?

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

2024-03-08 14:03脑子进煎鱼了 Golang

在本次对 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 所示:

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

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

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

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

如下代码变更:

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

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

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

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

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

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

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

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

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

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

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

总结

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

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

原文地址:https://mp.weixin.qq.com/s/RFsHfFByarSRAA-f1Rs13g

延伸 · 阅读

精彩推荐
  • Golang使用Go语言实现配置文件热加载功能

    使用Go语言实现配置文件热加载功能

    这篇文章主要介绍了使用Go语言实现配置文件热加载功能,以及配置文件热加载包的实现思路,需要的朋友可以参考下 ...

    python修行路6122020-05-14
  • Golang深入理解golang:内存分配原理

    深入理解golang:内存分配原理

    在说明golang内存分配之前,先了解下Linux系统内存相关的基础知识,有助于理解golang内存分配原理。 ...

    九卷2732020-11-04
  • GolangGo处理PDF的实现代码

    Go处理PDF的实现代码

    这篇文章主要介绍了Go处理PDF的实现代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小...

    poisoner10682020-06-02
  • Golang聊聊golang的defer的使用

    聊聊golang的defer的使用

    这篇文章主要介绍了聊聊golang的defer的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着...

    codecraft8152021-02-19
  • Golang基于gin的golang web开发:路由示例详解

    基于gin的golang web开发:路由示例详解

    这篇文章主要介绍了基于gin的golang web开发:路由示例详解,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参...

    陈宏博10082021-01-30
  • Golanggolang常用库之字段参数验证库-validator使用详解

    golang常用库之字段参数验证库-validator使用详解

    这篇文章主要介绍了golang常用库:字段参数验证库-validator使用,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借价值,需要的朋友可以...

    九卷13712021-01-26
  • Golang如何让shell终端和goland控制台输出彩色的文字

    如何让shell终端和goland控制台输出彩色的文字

    这篇文章主要介绍了如何让shell终端和goland控制台输出彩色的文字的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    Elonjelinek13072021-06-15
  • Golang深度解密 Go 语言中的 sync.Pool

    深度解密 Go 语言中的 sync.Pool

    sync.Pool 是 sync 包下的一个组件,可以作为保存临时取还对象的一个“池子”。这篇文章主要介绍了深度解密 Go 语言中的sync.Pool,本文给大家介绍的非常详...

    Stefno4642020-06-09