Goアプリケーションが起動すると、すべてのnon-testパッケージは特定の順序で初期化されます:最初に依存関係が順番に初期化され、その後にパッケージ自体が初期化されます。初期化には次のものが使用されます:
init関数は、パッケージ変数の初期化後に呼び出されます。パッケージがインポートされると、そのinitは変数/関数が使用される前に一度実行されます。
例:
var a = getA() func getA() int { fmt.Println("getAが呼び出されました") return 42 } func init() { fmt.Println("initが呼び出されました") } // 出力: // getAが呼び出されました // initが呼び出されました
特記事項:
任意のパッケージのinit関数を明示的に呼び出すことはできますか?
回答: いいえ、init関数はプログラムのコードから直接呼び出されることはありません。初期化プロセス中にランタイムによって所定のタイミングでのみ呼び出され、init()を明示的に呼び出すことは許可されていません - これはコンパイルエラーを引き起こします。
物語
プロジェクト内で、2つのパッケージが相互にインポートし、init()を介してグローバル変数を初期化しました。これにより循環依存関係とビルドエラーが発生し、Goは正しい初期化順序を決定できませんでした。
物語
難しいバグの原因は、init関数がまだ初期化されていないパッケージのグローバル変数を使用していたことでした。結果:不明瞭な動作が予測不可能な状態で発生しました。
物語
プロジェクトで副作用を目的として明示的に使用せずにパッケージをインポートしました(import _ "some/lib")。コードを再編成した後、必要なパッケージのinit関数が早すぎるタイミングで呼び出され(他のパッケージ内の依存変数の初期化前)、デバッグが難しいバグが発生しました。