1. tenntenn

    No comment

    tenntenn
Changes in body
Source | HTML | Preview
@@ -1,174 +1,174 @@
## はじめに
知られているようで、案外知られてないことをQiitaに投稿していくのも良いと思い、軽いネタですが`init`関数についてまとめたいと思います。
## init関数とは
`init`関数は特殊な関数で、パッケージの初期化に使われます。
以下のように`main`パッケージに書くと`main`関数より先に実行されます。
`main`パッケージでない場合は、`import`するだけで呼び出されます。
```
package main
import (
"fmt"
)
func init() {
fmt.Println("hello, init")
}
func main() {
fmt.Println("Hello, main")
}
```
[Playgroundで動かす](https://play.golang.org/p/Nsbs7YIbWp)
## init関数は何に使うのか?
上記では、初期化に使うと書きました。名前からしてそれは想像できますよね。
`init`関数は基本的にはパッケージ変数の初期化に用いられます。なぜならば、何かしらの副作用をパッケージに変数に与えなければ、呼んでも意味がないからです。もちろん、外部のリソースに何かしらの副作用を与える場合(サーバに何かリクエストしたり、ファイルに書き込んだり)もありますが、基本的にはパッケージ変数の初期化に使われます。
パッケージ変数は、代入文でも初期化を行うことがありますが、以下のような場合に`init`関数を使います。
* 代入文で表現できない初期化(forを使うなど)
* もちろん、`init`関数でやらず、別の関数を呼び出してそれを代入してもよい
* 初期化処理が複雑
* 他のパッケージのパッケージ変数を初期化してやる
* `image/png`や`image/jpeg`などでやっている
* main関数が使えない場合にエントリーポイントして使う
* GAE/Goはmain関数を作らず、`init`関数をエントリーポイントとして使用する
## init関数はいつ呼ばれるのか
以下のコードを実行すると、出力はどうなるでしょうか?
`Hello, playground`と出るのはなんとなく空気を読めばわかりますが、よく見るとパッケージ変数の初期化の方が先に実行されていることが分かります。
また、もちろん`main`関数より先に実行されています。
```
package main
import (
"fmt"
)
var msg = message()
func message() string {
return "Hello"
}
func init() {
fmt.Print(msg)
}
func main() {
fmt.Println(", playground")
}
```
[Playgroundで動かす](https://play.golang.org/p/_2pIMF9C9i)
[言語仕様](https://golang.org/ref/spec#Package_initialization)を読む限り、依存するパッケージがある場合(import文がある場合)には、それらのパッケージの初期化が終わった後に自身のパッケージの`init`関数が実行されるようです。
## init関数はいくつ書けるのか?
よくよく考えると、複数ファイルにまたがって、1つのパッケージに複数の`init`関数を書いていることがあるのに気づくことがあります。他の関数の場合は、複数定義すると、もちろんコンパイルエラーになります。
ちなみに、ファイルに1つという決まりもなく、複数書けるようになっています。
```
package main
import (
"fmt"
)
func init() {
fmt.Print("hello")
}
func init() {
fmt.Println(", init")
}
func main() {
}
```
[Playgroundで動かす](https://play.golang.org/p/ZaBbdTcHLY)
不思議ですね。`runtime`パッケージを使って関数名がどうなっているのか調べてみましょう。
```
package main
import (
"fmt"
"runtime"
)
func init() {
var pcs [1]uintptr
runtime.Callers(1, pcs[:])
fn := runtime.FuncForPC(pcs[0])
fmt.Println(fn.Name())
}
func init() {
var pcs [1]uintptr
runtime.Callers(1, pcs[:])
fn := runtime.FuncForPC(pcs[0])
fmt.Println(fn.Name())
}
func main() {
var pcs [1]uintptr
runtime.Callers(1, pcs[:])
fn := runtime.FuncForPC(pcs[0])
fmt.Println(fn.Name())
}
```
[Playgroundで動かす](https://play.golang.org/p/4r1-rD0yE9)
実行すると、以下のように出力されます。
```
main.init.1
main.init.2
main.main
```
どうやら、各`init`関数には番号が付けられており、区別されているようです。
## init関数は呼び出せるのか
`init`関数は他の関数内から呼び出せるんでしょうか?
`main`関数から呼び出してみます。
```
package main
import "fmt"
func init() {
fmt.Println("hoge")
}
func main() {
init()
}
```
[Playgroundで動かす](https://play.golang.org/p/9agxBo8o4Q)
実行すると、以下のようにエラーがでます。
```
tmp/sandbox671239315/main.go:10: undefined: init
```
定義されていないことになるようです。なるほど!
-## まとめ
+## おわりに
`init`関数についてまとめてみました。
ちなみに、この記事を書こうと思った元ネタは[こちら](https://talks.godoc.org/github.com/davecheney/presentations/gopher-puzzlers.slide)です。これも面白いので、ぜひ読んでみて下さい!