为什么 Go 不支持循环引用?

polarisxu

共 1838字,需浏览 4分钟

 ·

2021-11-17 23:15

大家好,我是煎鱼。

学习 Go 语言的开发者越来越多了,很多小伙伴在使用时,就会遇到种种不理解的问题。

其中一点就是包的循环引用的报错:

package command-line-arguments
 imports github.com/eddycjy/awesome-project/a
 imports github.com/eddycjy/awesome-project/b
 imports github.com/eddycjy/awesome-project/a: import cycle not allowed

一下子就很懵逼了,为什么 Go 不支持包之间的循环引用呢,这就很不解了,难道还影响性能了?

如下图:

图来自网络

今天煎鱼将和大家一起了解背后的原因。

案例演示

这里我们做一个基本的案例 Demo,便于没接触过的同学建立初步认知。我们的程序分别有 2 个 package。

package a 的代码如下:

import (
 "github.com/eddycjy/awesome-project/b"
)

func Hello(s string) {
 b.Print(s)
}

package b 的代码如下:

import (
 "fmt"

 "github.com/eddycjy/awesome-project/a"
)

func Hello() {
 a.Hello("脑子进煎鱼了")
}

func Print(s string) {
 fmt.Println(s)
}

再在 main.go 的文件中调用 a.Hello("脑子进煎鱼了") 方法。

一运行,就会出现如下错误提示:

package command-line-arguments
 imports github.com/eddycjy/awesome-project/a
 imports github.com/eddycjy/awesome-project/b
 imports github.com/eddycjy/awesome-project/a: import cycle not allowed

错误的本质原因是 package a 引用了 package b,而 package b 又引用了 package a,造成了循环引用。

这在 Go 语言中是明令禁止的,在编译时就会中断程序,导致编译失败。

原因分析

根据现在 Go 官方的统一意见来看,package 循环导入几乎不可能出现,即使是 Go2,也被明确拒绝了。

因为 Go2 可能是很多核心问题的破变的关键节点,有许多人提了类似《proposal: Go 2: allow import cycle》的提案,希望解决循环引入的问题。

Go 语言之父 Rob Pike 亲自回答了这个问题,原因如下:

  • 没有支持循环引用:目的是迫使 Go 程序员更多地考虑程序的依赖关系。
    • 保持依赖关系图的简洁。
    • 快速的程序构建。
  • 如果支持循环引用:很容易会造成懒惰、不良的依赖性管理和缓慢的构建。这是设计者不希望看见的。
    • 混乱的依赖关系。
    • 缓慢的程序构建

因此考虑一开始就保持依赖图的正确 DAG,Rob Pike 认为这是一个值得预先简化的领域。

在 Go 程序中去做导入循环这件事可能很方便,但背后的代价可能是灾难性的,会对 Go 的构建性能和依赖系造成非常不利的影响

所以在 Go 中被明确禁止支持。

总结

在程序中,如果我们频繁的出现模块与模块之间的循环引用,这时候我们是不是应该考虑一下,是不是设计的有些问题,要不要考虑调整?

但也并非所有的事都是二极管,Go 源码可能或多或少都有自己循环引用的案例,最重要的是想清楚。

你对此支持循环引用怎么看,欢迎在评论区留言交流:)

关注煎鱼,吸取他的知识 👆



你好,我是煎鱼。高一折腾过前端,参加过国赛拿了奖,大学搞过 PHP。现在整 Go,在公司负责微服务架构等相关工作推进和研发。

从大学开始靠自己赚生活费和学费,到出版 Go 畅销书《Go 语言编程之旅》,再到获得 GOP(Go 领域最有观点专家)荣誉,点击蓝字查看我的出书之路

日常分享高质量文章,输出 Go 面试、工作经验、架构设计,加微信拉读者交流群,记得点赞!

浏览 39
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报