😺

Goのinit関数の役割と特別性

2024/08/31に公開

現在、Goを使ってwebサービスのbackendを書いている中でinit関数を扱ったので、忘備録として書いておきます。
以下がその時に書いたコードのinit()部分を抜き出した部分です。

var jwtKey *ecdsa.PrivateKey

func init() {
	privateKeyPath := os.Getenv("JWT_SECRET") // 環境変数には鍵のpathを設定
	keyData, err := ioutil.ReadFile(privateKeyPath)
	if err != nil {
		panic("failed to read private key file")
	}

	block, _ := pem.Decode(keyData)
	if block == nil || block.Type != "EC PRIVATE KEY" {
		panic("failed to decode PEM block containing ECDSA private key")
	}

	privateKey, err := x509.ParseECPrivateKey(block.Bytes)
	if err != nil {
		panic("failed to parse ECDSA private key")
	}
	jwtKey = privateKey
}

上記のinit()はECDSA秘密鍵が書いているファイルのPathを環境変数から読み込み、秘密鍵をグローバル変数のjwtKeyに代入しています。

このコードでは、init()を明示的に実行している場所がありません。

一般的な関数

通常の関数は定義をしたら、下記のように関数名を書かないと実行されません。

package main

import "fmt"

func test() {
	for i := 1; i <= 100; i += 1 {
		fmt.Println(i)
	}

}

func main() {
	test()
}

test()をコメントアウトしたら、実行しても何も表示されません。

init()

しかし、下記のように関数名をinitに変えるとmain()にinit()と書かなくても実行されます。

package main

import "fmt"

func init() {
	for i := 1; i <= 100; i += 1 {
		fmt.Println(i)
	}

}

func main() {
}

init()の実行ルール

実行されるルールとしては以下のとおりです。
init()が書かれたpackageが初めて使用されるタイミングであり、package内の他のコードよりも先に実行される。
また、package内で複数のinit()を定義できる。実行される順番としては定義された順に実行される。

Discussion