当 Go 应用程序启动时,所有非测试包都会严格按特定顺序初始化:首先初始化所有被导入的依赖(按链),然后是包本身的初始化。初始化时使用:
init 函数在包变量初始化后被调用。如果包被导入,init 会在使用该包的变量/函数之前执行一次。
示例:
var a = getA() func getA() int { fmt.Println("getA called") return 42 } func init() { fmt.Println("init called") } // 输出: // getA called // init called
特点:
是否可以显式调用任何包的 init 函数?
答案: 不,init 函数永远不会被程序代码直接调用。它仅在包初始化过程中的预定时刻由运行时调用,显式调用 init() 是不允许的——会导致编译错误。
故事
在这个项目中,两个包相互导入以通过 init() 初始化全局变量。这导致了循环依赖和构建错误——Go 无法确定正确的初始化顺序。
故事
复杂错误的原因是 init 函数使用了那些尚未初始化的包的全局变量。结果:运行时产生不确定的行为和不可预测的状态。
故事
在项目中为副作用导入了包而没有明确使用(通过 import _ "some/lib")。在代码重组后,所需包的 init 函数被过早调用(在另一个包中依赖变量初始化之前),出现了难以调试的错误。