如果我在同一个Go包的两个文件的顶部导入”fmt”,它会被编译两次吗?

huangapple go评论110阅读模式
英文:

If I import "fmt" at the top of 2 files in the same Go package, will it compile it twice?

问题

我正在使用go 1.6(我需要更新)。假设我在一个包中有两个文件,并且在这两个文件中都使用了fmt包。你必须在这两个文件中都显式导入fmt,否则编译器会报错,那么这是否意味着它会将fmt二进制文件存储在我的可执行文件中两次?

英文:

I'm using go 1.6 (I need to update). Let's say I have 2 files in a package, and I'm using the fmt package in both of them. You have to explicitly import fmt in both of those files, or the compiler throws and error, so does that mean it stores the fmt binary in my executable twice?

答案1

得分: 5

不,那将是一个非常糟糕的设计(在最后的异常中解释)。顺便说一下,检查这一点非常容易,创建一个名为a的包,包含两个文件:aa.goab.go。两个文件都导入fmt包。生成可执行文件(例如go build,必须在main包上调用),并检查其大小。现在将ab.go中使用fmt包的所有代码移动到aa.go中,并从ab.go中删除fmt导入。再次生成可执行文件。它们的大小将相同。

包是通过其完整路径来标识的。无论有多少个(相同包的)文件或多少个(不同的)包引用一个包,该包在最终的可执行二进制文件中只会被包含一次。

还要注意,并非所有被使用的包中的内容都会被包含在可执行二进制文件中。某些未使用/未引用的内容可能会被排除。有关详细信息,请参阅https://stackoverflow.com/questions/38875016/splitting-client-server-code/38875901#38875901和https://stackoverflow.com/questions/42825926/how-to-remove-unused-code-at-compile-time?noredirect=1&lq=1

不过,有一个例外情况,那就是Go 1.8引入的插件。被插件引用的包将包含在编译的插件中,因为插件无法保证在加载它的可执行二进制文件中会有哪些包可用。因此,如果fmt包被main包和一个插件引用,fmt包的代码将同时包含在两者中(可执行二进制文件和编译的插件)中。需要注意的是,即使fmt包的代码在可执行文件和插件中都存在两次,但在运行时(内存中)仍然只有一个包的"实例"(例如,它的全局变量只有一个实例,并且只会初始化一次)。

一个"特殊"情况是vendored packages(供应商包)。如果一个包被放置在vendor文件夹中,并且一个包引用了这个供应商包,那么它被认为是与原始包(供应商包)不同的包。如果原始包也被另一个包引用,那么两者都将包含在可执行二进制文件中(但它们被视为不同的包,供应商包的完整路径与原始包的完整路径不同)。

英文:

No, that would be a really poor design (read exception at the end). By the way, it is very easy to check this, create a package named a, consisting of 2 files: aa.go and ab.go. Have both import fmt. Produce the executable (e.g. go build, must called on the main package) and check its size. Now move all code from ab.go that uses the fmt package to aa.go, and remove the fmt import from ab.go. Produce the executable again. They will have the same size.

Packages are identified by their full path. No matter how many files (of the same package) or how many (different) packages refer to a package, the package will only be included once in the final executable binary.

Also note that not everything will be included from used packages in the executable binary. Certain things that are not used / referred to may be excluded. For details see https://stackoverflow.com/questions/38875016/splitting-client-server-code/38875901#38875901; and https://stackoverflow.com/questions/42825926/how-to-remove-unused-code-at-compile-time?noredirect=1&lq=1

There is one exception though, which is the plugins introduced in Go 1.8. Packages referred to by plugins will be included in the compiled plugin –they have to be–, because the plugin has no guarantee what packages will be available in the executable binary that will load it. So if the fmt package is referred to by package main, and also by a plugin, the code of the fmt package will be included in both (in the executable binary and in the compiled plugin). It should be noted that even though the fmt package's code will be present twice (once in the executable and once in the plugin), there will still be only one "instance" of the package in the runtime (in memory) (e.g. its global variables will have one instance, and it will only be initialized once).

An "edge" case is vendored packages. If a package is vendored in a vendor folder, and a package refers to this vendored package, that is considered distinct from the original (that is vendored), and if the original is also referred to by another package, both will be included in the executable binary (but they are not considered the same, the full path of the vendored is different from the full path of the original).

huangapple
  • 本文由 发表于 2017年6月11日 01:55:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/44476388.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定