Identifier main is ubiquitous. Every Go program starts in a package main by calling identically named function. When this function returns the program ends its execution. Functions init also play special role and this post will describe their properties and introduce how to use them.

init functions are defined in package block and are used for:

variables initialization if cannot be done in initialization expression,

checking / fixing program’s state,

registering,

running one-time computations,

and many more.

Besides some differences discussed below you can put there anything which is valid inside regular function.

Package initialization

To use a imported package it needs to be initialized first. It’s done by Golang’s runtime system and consists of (order matters):

initialization of imported packages (recursive definition) computing and assigning initial values for variables declared in a package block executing init functions inside the package

Package initialization is done only once even if package is imported many times.

The order

Package in Go can contain many files. What is the order of variables initialization and init functions calls if they’re spread across many package’s files? First, initialization dependency machinery kicks in (more in “Initialization dependencies in Go”). When this done there needs to be a decision made if initialize variables in file a.go or maybe z.go should be handled earlier. This depends on the order of files presented to the compiler. If z.go is first passed by build system then variables initializations are done there before doing the same in a.go. The same applies to triggering init functions. Language specification recommends to always use the same order and pass file names from package in lexical order:

To ensure reproducible initialization behavior, build systems are encouraged to present multiple files belonging to the same package in lexical file name order to a compiler.

but relying on particular order is rather a path for less portable programs. Let’s see an example how it all works together:

sandbox.go

package main import "fmt" var _ int64 = s() func init() {

fmt.Println("init in sandbox.go")

} func s() int64 {

fmt.Println("calling s() in sandbox.go")

return 1

} func main() {

fmt.Println("main")

}

a.go

package main import "fmt" var _ int64 = a() func init() {

fmt.Println("init in a.go")

} func a() int64 {

fmt.Println("calling a() in a.go")

return 2

}

z.go

package main import "fmt" var _ int64 = z() func init() {

fmt.Println("init in z.go")

} func z() int64 {

fmt.Println("calling z() in z.go")

return 3

}

Program outputs:

calling a() in a.go

calling s() in sandbox.go

calling z() in z.go

init in a.go

init in sandbox.go

init in z.go

main

Properties

init function doesn’t take arguments neither returns any value. In contrast to main, identifier init is not declared so cannot be referenced:

package main import "fmt" func init() {

fmt.Println("init")

} func main() {

init()

}

and it gives “undefined: init” error while compilation.