Getting started with Go init() function
When developing applications with Go, you often have to define or declare the state of your application.init()
function in Go is used to declare the state of an application such as initializing connection to the database, initializing logs and log levels or exporting cloud credentials. There are more use cases for the init()
function and they are not limited to the mentioned use cases.
Each Go package can optionally have a private function named init() that is automatically executed at the beginning of execution time—init() runs when the package is initialized at the beginning of program execution. The init()
function has the following characteristics:
- It takes no arguments.
- It returns no values.
- The
init()
function is optional. - The
init()
function is called implicitly by Go. - You can have an
init()
function in the main package. In that case,init()
is executed before themain()
function. In fact, allinit()
functions are always executed prior to themain()
function. - A source file can contain multiple
init()
functions—these are executed in the order of declaration. - The
init()
function or functions of a package are executed only once, even if the package is imported multiple times. - Go packages can contain multiple files. Each source file can contain one or more
init()
functions.
Order of execution for init() function in golang
Following image explains the flow of execution for init()
function:
As an example, if a main package imports package A and package A depends on package B, then the following will take place:
- The process starts with
main
package. - The main package imports package A.
- Package A imports package B.
- The global variables, if any, in package B are initialized.
- The
init()
function or functions of package B, if they exist, run. This is the firstinit()
function that gets executed. - The global variables, if any, in package A are initialized.
- The
init()
function or functions of package A, if there are any, run. - The global variables in the
main
package are initialized. - The
init()
function or functions ofmain
package, if they exist, run. - The
main()
function of themain
package begins execution.
This is also shown as a practical example later in this article.
init() function syntax
init()
function is quite similar to the main()
function with regards to their syntax and declaration. init()
function does not take any arguments. When declared in a package , the init()
function will always get executed before the main()
function. This enables the main()
function to work with variables that have been initialized already. Create a main.go
file in your working directory and add the below code sample.
Example
package main
import "fmt"
var greetings string
var age int
func init() {
fmt.Println("I always execute before main() function")
greetings = "Hello world"
}
func main() {
fmt.Println("I execute after init() function")
fmt.Println(greetings)
fmt.Printf("Go language is %d years old \n", age)
}
Explanation
To test the code, move to your terminal and navigate to your working directory and enter go run main.go
.We declare two variables,greetings
and age
. We then define the init()
function whose main job is to initialize the greetings
variable. The greetings variable gets initialized in the init()
function while the age
variable does not. We then print the logs in the terminal using the main()
function. It is important to note that the init()
function will always be executed before the main()
function. This has been demonstrated in the code example. In the output the age
does not get assigned any value other than 0 values that it was assigned when declaring it with var age int
.
Output
I always execute before main() function
I execute after init() function
Hello world
Go language is 0 years old
Multiple init() functions in a file
It is possible to have more than one init()
functions declared in the same file. The order in which these multiple init()
functions will be executed determines the behavior of the application. Go determines the order of execution based on the order in which init()
functions have been defined in the file, as shown in the example.
Example
package main
import "fmt"
func init() {
fmt.Println("<<< First >>>")
}
func init() {
fmt.Println("<<< Second >>>")
}
func init() {
fmt.Println("<<< Third >>>")
}
func main() {
fmt.Println("I execute after init() functions")
}
Explanation
When this code is executed, the init()
functions will be executed in their respective order of declaration such that the first function prints on the console , followed by the second init()
function and lastly the third init()
function. Multiple declaration of init()
functions are often used in complex systems. It enables teams to break complex initialization application states into multiple init() functions that are easy to read.
Output
<<< First >>>
<<< Second >>>
<<< Third >>>
I execute after init() functions
Multiple packages with init() functions
Some applications can get complex and might require declaration of the init()
function in different files within a package. It is possible to declare multiple init()
functions within a file or package. In Go, when multiple files are encountered, they are processed alphabetically. We therefore need to keep in mind how we name our Go files that will house init()
functions.
For example, suppose we have package a , package b and package main, and package main imports package a and package b, below will be the order of execution.
- Global variables in package a will be initialized and then
init()
function in package a will be executed. - Global variables in package b will be initialized and then
init()
function in package b will be executed. - Global variables in main package will be initialized and
init()
function in package main will be executed - Finally the main function will start executing.
To test the above explanation, we need to first of all create a module that will keep track of our code in different packages. To create a module in Go, navigate to your working directory in the terminal and issue this command:
$ mkdir sample && cd sample $ go mod init example.com/go_init_func
go.mod
module example.com/go_init_func
go 1.18
sample/a/a.go
package a
func init() {
println("init() function in a/a.go")
}
func Greetings() {
println("Hello, world from a/a.go")
}
sample/b/b.go
package b
import "fmt"
func init() {
fmt.Println("init() function in b/b.go")
}
func Greetings() {
fmt.Println("Hello, world from b/b.go")
}
sample/main.go
func main() {
fmt.Println("Executing main() function in main.go")
a.Greetings()
b.Greetings()
}
Explanation
The above example stresses the point that the init()
function in different packages will be executed in an order that is determined by the package names. In our example a/a.go
package gets executed first, then b/b.go
package follows afterwards. In the main()
function, b.Greetings()
is called first , followed by a.Greetings()
. We would expect logs for b/b.go
to be printed first, but a/a.go
logs get printed first then b/b.go
package logs follow.
Output
init() function in a/a.go
init() function in b/b.go
init() function in main.go
Executing main() function in main.go
Hello, world from a/a.go
Hello, world from b/b.go
Challenges working with init() function
It is a challenge to name files alphabetically in order to obey package initialization specification in Go. This will create problems in your code because it's common practice to rename files in your code, and doing so it will affect the order in which init()
functions will be processed. One way to prevent having this kind of a scenario is to have all init()
functions declared in one file. The Go compiler will load them in the order they have been declared in the file.
Summary
We have learnt that the Go init()
function is useful when defining the state of your Go application. It is possible to define multiple init()
functions in one file and multiple packages with init()
functions.
init()
functions in a single file will be loaded in the order that they have been declared, while init()
functions in multiple packages will be loaded based on the file names used in alphabetical order. Declaring init()
functions in multiple packages is not desirable , therefore we should define our init()
functions in a single file. Defining init()
functions in a single file , enables us to comfortably understand the behavior of our application even after renaming our files.
References
https://go.dev/ref/spec#Package_initialization