2022年12月5日 星期一

golang 的 init()

之前就很好奇,像是 github.com/go-sql-driver/mysql 到底是如何做到只需寫以下幾行,就能夠連線 mysql

import (
	"database/sql"

	_ "github.com/go-sql-driver/mysql"
)

// ...

db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306/my-db)")

後來去查,原來是因為 init() 這個 function

根據我查到的資料, init() 雖然是 private function,但和 main() 一樣,都是特殊 function

main() 是程式的主程式; init() 則是在 main() 之前執行,每個 import 進來的 package 都會執行的 function


例如說以下這個 go module:

package main

import (
	_ "play-ground/foobar"
)

func main() {

}
-- foo/foo.go --
package foo

import "fmt"

func init() {
	fmt.Println("foo")
}
-- bar/bar.go --
package bar

import "fmt"

func init() {
	fmt.Println("bar")
}
-- foobar/foobar.go --
package foobar

import (
	_ "play-ground/bar"
	_ "play-ground/foo"
)
-- go.mod --
module play-ground

因為 main 有引用到 play-ground/foobar,而 foobar 又引用了 foo 和 bar 兩個 package,依照順序,雖然 main 並沒有 print 任何東西,但最終程式會輸出:

bar
foo

而 init() 還有一個特殊性——一個 package 允許多個 init()

故以下的程式是正常的,且兩個 init() 都能執行

package main

import (
	_ "play-ground/foo"
)

func main() {

}
-- foo/foo.go --
package foo

import "fmt"

func init() {
	fmt.Println("1st init")
}
func init() {
	fmt.Println("2nd init")
}
-- go.mod --
module play-ground


參考資料

asong, (2021, June 7). Go语言中的神奇函数init. Golang梦工厂. https://asong.cloud/go%E8%AF%AD%E8%A8%80%E4%B8%AD%E7%9A%84%E7%A5%9E%E5%A5%87%E5%87%BD%E6%95%B0init/

沒有留言:

張貼留言