英文:
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.go
和ab.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).
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论