GuidesUnderstanding the init Function in Go

Understanding the init Function in Go

There are two predefined functions reserved for special purposes in Go. One is init() and the other one is main(). The init() function is typically used to initialize a specific state of the application. Although useful, it sometimes makes code difficult to read. Here, we take a quick look at the init() function, its uses, and the concepts behind it, alongside some Golang coding examples.

Before we begin, if you are new to Go or want to revisit some Go concepts, feel free to check out some of the following Golang programming tutorials from our Go series:

The init() Function in Go and Golang

The init() function is reserved and is used for specific reasons. This function is defined to take no arguments and return nothing. This function is meant to run before any other piece of code – even before the main() function, which supposedly is the first function from where execution of our program begins. The init() function serves some specific purposes such as to initialize a specific state of the application. For example, suppose we want to find out the type of operating system currently running before executing our program. We could do so using the following Go code:

package main

import (
	"fmt"
	"os"
	"runtime"
)

func init() {
	if runtime.GOOS == "linux" {
		fmt.Println("This program will run.")
	} else {
		fmt.Println("Program will exit now.")
		os.Exit(1)
	}

}

func main() {
	fmt.Println("running main function")
}

If you run this in your code editor or IDE, you would get the following output:

This program will run.
running main function

The Go’s runtime package provides the GOOS (Go Operating System) string constant to identify the operating system the program is currently running on. Typical values of this constant are Linux, Windows, Darwin (Mac OS X), and Freebsd.

How to Define init()

Observe that the init() function in the example above is automatically called prior to running the main() function and checks the runtime. GOOS constant. Whenever the init() function is declared in code, Go loads and runs it prior to anything else in the package. A package, however, can have more than one init() function and all of them are executed before the main package’s main() function. Also note that the init() function is never called explicitly. It is called implicitly by the Go runtime. Unlike main(), the init() function is optional but can occur multiple times in a package; on the other hand, every program must have a single main() function in the main package.

In the case of multiple init() functions, the order of their declaration matters in the code, because they are executed exactly in that order. So, if you want one init() function to execute first, then it must be the first function declared in the package. Here is a quick example to illustrate the idea:

package main

import (
	"fmt"
)

func init() {
	fmt.Println("Veni.")
}

func init() {
	fmt.Println("Vidi.")
}

func init() {
	fmt.Println("Vici.")
}

func main() {
	fmt.Println("Running main function")
}

This produces the following output:

Veni.
Vidi.
Vici.
Running main function

Now, change the order of the init() function and compile and execute the same code. Run the code sample to see how the output changes.

What is the init() Function Used for in Go?

The init() function can have numerous uses – specifically when you want something to execute or initialize before the main() function executes. One of its typical uses is when importing a package. A specific setup task can be accomplished prior to allowing the use of a package. The following is a simple example illustrating this idea:

package randgreet

import (
	"math/rand"
	"time"
)

var greet = []string{"Good Morning", "Guten Morgen", "Bonjour", "สวัสดีตอนเช้า", "Chào buổi sáng", "өглөөний мэнд"}

func init() {
	rand.Seed(time.Now().UnixNano())
}

func RandGreetLang() string {
	return greet[rand.Intn(len(greet))]
}


package main

import (
	"fmt"

	"example.com/mano/gtk_proj1/randgreet"
)

func main() {
	fmt.Println(randgreet.RandGreetLang())
	fmt.Println(randgreet.RandGreetLang())
	fmt.Println(randgreet.RandGreetLang())
}

This produces the following output when run in your code editor:

Chào buổi sáng
Guten Morgen
สวัสดีตอนเช้า

In the above example there are two packages. The main package imports the randgreet package and invokes RandGreetLang. This function returns a greetings message in string format. The greetings language is chosen in a random fashion from a list represented by a string array. The random function from the math package is initialized with current time as its seed value.

Now, what we want is that whenever the randgreet package is imported, we want to ensure that the seed value for the random function is initialized. This can only be accomplished by using the init() function and that is what we have done in our code. Had we initialized the random seed inside the RandGreetLang function, it would have been executed every time this function is invoked (observe how we have invoked the RandGreetLang function three times in main). This is not what we want. We simply want the random function to be initialized only once. The init() function makes this possible and is executed only once when the package is imported. This type of scenario is a suitable use for the init() function.

Another example scenario where the init() function may be put to good use is this: suppose your program processes a picture and takes an image file as an input. There are different types of images: JPEG, PNG, GIF, and so forth. Each must be processed differently. Therefore, prior to processing the main function, the init can ensure the type of image file and direct the appropriate course of action by the program.

Problems with init()

We know that multiple init() functions declared in a single file are executed in the order of their declaration. That’s fine, but what if the init() function is declared across multiple files. What would be the order of execution then? According to the Go language specification, init() functions declared across multiple files in a package are processed in alphabetical order of the file name. For example, the init() declaration in a.go file would be processed prior to the init() function declared in file b.go.

This behavior may sometimes become a problem because a simple renaming of a file can alter the init() function execution order, which may have undesirable effects. The way to eradicate this problem is by declaring all init() functions within a single file or ensuring that file names maintain a lexical order within the same package.

Although it is possible, init() functions should not be allowed to change or manage any global variables that affect the state of the package. A variable made available to the entire package has a direct stake on the package state. Any undesirable changes from any of the init() function may destabilize the predictability of the program. In fact, global variables have a bad reputation of unintended misuse. But they cannot be discarded altogether because in some cases they help in sharing the values across functions. Good programming practice is to use global variables less often than one needs them. Variables are safer with as local accessibility as possible.

Golang init()

In Go, init() is a very special function meant to execute prior to the main() function. Unlike main(), there can be more than one init() function in a single or multiple files. For multiple init in a single file, their processing order is in the order of their declaration, while init declared in multiple files are processed according to the lexicographic filename order. The init() function is optional. It is totally fine to write a workable program without a single trace of the init() function. It is particularly used for initialization and setting up of the program or any other purpose deemed fit. A word of caution though – misusing init() can be problematic.

Read more Go and Golang programming tutorials.

Latest Posts

Related Stories