The init function is a special function in Go that runs automatically before main() to set up package state, and it executes in a specific order: imports first (depth-first), then the package's own init functions. You cannot call init manually, pass arguments to it, or return values from it; it exists solely for side effects like registering handlers or initializing global variables.
The initialization order follows a strict depth-first traversal of the import graph. When a package is imported, Go initializes all its dependencies first. Within a single package, if multiple init functions exist, they run in the order they appear in the source file (lexicographical order across files).
Here is a practical example demonstrating the order:
// package main
package main
import (
"fmt"
"myapp/utils" // This triggers utils.init() before main.init()
)
func init() {
fmt.Println("main init")
}
func main() {
fmt.Println("main function")
}
// package utils
package utils
import (
"fmt"
)
func init() {
fmt.Println("utils init")
}
Output:
utils init
main init
main function
In this scenario, the utils package is imported by main. Go recursively initializes utils (and any dependencies utils might have) before executing the init function in main. If utils imported another package, that would run first.
Use init sparingly. It is best suited for tasks that cannot be done in main or constructors, such as:
- Registering plugins or drivers (e.g.,
database/sqldrivers). - Initializing global state that depends on other packages being ready.
- Validating environment variables or configuration at startup.
Avoid using init for complex logic or heavy computation. If you need to initialize state based on arguments or return errors, use a constructor function (e.g., New()) instead. This keeps your code testable and explicit.
// Bad: Complex logic in init
func init() {
if err := loadConfig(); err != nil {
// Cannot return error, must panic or ignore
panic(err)
}
}
// Good: Explicit constructor
func NewConfig() (*Config, error) {
return loadConfig()
}
Remember that init functions run before main, so they cannot depend on command-line arguments parsed in main. If you need CLI flags, parse them in main and pass them to your initialization logic.