Go Fundamentals - Sample

Table of Contents

Chapter 5.4: Init

During initialization of a .go file, the compiler will execute any init functions it finds in the file before the main function.

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

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

$ go run .

init
main

--------------------------------------------------------------------------------
Go Version: go1.22.0
Listing 5.1: A basic program with an init() function.

Multiple Init Statements

Unlike all other functions, that can only be declared once, the init() function can be declared multiple times throughout a package or file. However, multiple init()s can make it difficult to know which one has priority over the others.

When declaring multiple init functions within the same file the init() functions will execute in the order that you encounter them. In Listing 5.2 the four init functions are executed in the order that they are declared in the file.

package main

import "fmt"

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

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

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

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

func main() {}

$ go run .

First init
Second init
Third init
Fourth init

--------------------------------------------------------------------------------
Go Version: go1.22.0
Listing 5.2: Multiple init() functions in the same file.

Init Order

If you have multiple init() declarations in a package in different files, the compiler will run them based on the order in which it loads files.

Given the directory structure in Listing 5.3, if you had an init() declaration in a.go and b.go, the first init() to run would be from a.go. However, if you renamed a.go to c.go, the init() from b.go would now run first.

Care must be taken if any init() declarations have an order of precedence when running and exist in different files.

└── cmd
    ├── a.go
    ├── b.go
    └── main.go
Listing 5.3: A directory structure with init() declarations in different files.

Using init Functions for Side Effects

In Go, it is sometimes required to import a package not for its content, but for the side effects that occur upon importing the package. This often means that there is an init() statement in the imported code that executes before any of the other code, allowing for the developer to manipulate the state in which their program is starting. This technique is called importing for a side effect.

A common use case for importing for side effects is to register functionality in your code, which lets a package know what part of the code your program needs to use. In the image package, for example, the image.Decode function needs to know which format of image it is trying to decode (jpg, png, gif, etc.) before it can execute. You can accomplish this by first importing a specific program that has an init() statement side effect.

Consider you are trying to use image.Decode on a .png file with the function in Listing 5.4.

func decode(reader io.Reader) (image.Rectangle, error) {

	// decode the image reader
	m, _, err := image.Decode(reader)
	if err != nil {

		// return the error
		return image.Rectangle{}, err
	}
	return m.Bounds(), nil
}
Listing 5.4: A program that uses image.Decode to decode a .png file.

A program with this code will still compile, but any time we try to decode a png image, we will get an error.

$ go run .

image: unknown format

exit status 1

--------------------------------------------------------------------------------
Go Version: go1.22.0
Listing 5.5: Compilation error trying to decode a png.

To fix this, we need to first register the .png image format for image.Decode to use. When imported, the image/png package, which contains the init() statement in Listing 5.6, will be call and the package will register itself with the image package as an image format.

func init() {
    image.RegisterFormat("png", pngHeader, Decode, DecodeConfig)
}
Listing 5.6: An init() statement in the image.png package registering the .png image format.

In Listing 5.7, we import image.png package into our application, then the image.RegisterFormat() function in image/png will run before any of our code and register the .png format before we try and use it.

import (
	"fmt"
	"image"
	_ "image/png"
	"io"
	"os"
)

$ go run .

decoded image bounds: (0,0)-(60,60)

--------------------------------------------------------------------------------
Go Version: go1.22.0
Listing 5.7: A program that uses image.Decode to decode a .png file.

While this use of import statements and init functions can be found in numerous places in the standard library, this is considered an anti-pattern in Go and should never be used. Always be explicit in your code.