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 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:

Advertisement
  • 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 the main() function. In fact, all init() functions are always executed prior to the main() 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:

How init() function works in GoLang with Execution Flow

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 first init() 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 of main package, if they exist, run.
  • The main() function of the main 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

Advertisement

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 agevariable 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.

  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 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

 

Didn't find what you were looking for? Perform a quick search across GoLinuxCloud

If my articles on GoLinuxCloud has helped you, kindly consider buying me a coffee as a token of appreciation.

Buy GoLinuxCloud a Coffee

For any other feedbacks or questions you can either use the comments section or contact me form.

Thank You for your support!!

Leave a Comment

X