How init() function works in GoLang with Execution Flow

How init() function works in GoLang with Execution Flow

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 theinit()function and they are not limited to the mentioned use cases.

Each Go package can optionally havea private function namedinit()that is automatically executed at the beginning of execution time—init()runs when the package is initialized at the beginning of program execution. Theinit()function hasthe following characteristics:

  • It takes no arguments.
  • Itreturns no values.
  • Theinit()function is optional.
  • Theinit()function is called implicitly by Go.
  • You can have aninit()function in themainpackage. In that case,init()is executedbeforethemain()function. In fact, allinit()functions are always executed prior to themain()function.
  • A source file can contain multipleinit()functions—these are executed in the order of declaration.
  • Theinit()function or functions of a package are executed only once, even ifthe package is imported multiple times.
  • Go packages can contain multiple files. Each source file can contain one or moreinit()functions.

Order of execution for init() function in golang

Following image explains the flow of execution for init() function:

image

As an example, if amainpackageimports packageAand packageAdepends on packageB, then the following will take place:

  • The process starts withmainpackage.
  • Themainpackage imports packageA.
  • PackageAimports packageB.
  • The global variables, if any, in packageBare initialized.
  • Theinit()function or functions of packageB, if they exist, run. This is the firstinit()function that gets executed.
  • The global variables, if any, in packageAare initialized.
  • Theinit()function or functions of packageA, if there are any, run.
  • The global variables in themainpackage are initialized.
  • Theinit()function or functions ofmainpackage, if they exist, run.
  • Themain()function of themainpackage 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 , theinit() function will always get executed before themain()function. This enables themain()function to work with variables that have been initialized already. Create amain.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 theinit()function whose main job is to initialize thegreetings****variable. The greetings variable gets initialized in theinit()function while theagevariable does not. We then print the logs in the terminal using themain() function. It is important to note that theinit()function will always be executed before themain()function. This has been demonstrated in the code example. In the output theage****does not get assigned any value other than 0 values that it was assigned when declaring it withvar 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 oneinit()functions declared in the same file. The order in which these multipleinit()functions will be executed determines the behavior of the application. Go determines the order of execution based on the order in whichinit()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, theinit()functions will be executed in their respective order of declaration such that the first function prints on the console , followed by the secondinit() function and lastly the thirdinit()function. Multiple declaration ofinit()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.

  1. Global variables in package a will be initialized and then init() function in package a will be executed.
  2. Global variables in package b will be initialized and then init() function in package b will be executed.
  3. Global variables in main package will be initialized and init() function in package main will be executed
  4. 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 theinit()function in different packages will be executed in an order that is determined by the package names. In our examplea/a.go package gets executed first, thenb/b.go package follows afterwards. In themain()function,b.Greetings()is called first , followed bya.Greetings(). We would expect logs forb/b.go to be printed first, buta/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 whichinit()functions will be processed. One way to prevent having this kind of a scenario is to have allinit()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 Goinit()function is useful when defining the state of your Go application. It is possible to define multipleinit()functions in one file and multiple packages withinit()functions.

init()functions in a single file will be loaded in the order that they have been declared, whileinit()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 ourinit()functions in a single file. Defininginit()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

Antony Shikubu

Antony Shikubu

Systems Integration Engineer

Highly skilled software developer with expertise in Python, Golang, and AWS cloud services.