When does init() run in Go? Analyzing with a real example
Imagine: you're writing a microservice in Go. In one of the packages, you declared init() to load the config. You launch the application — and suddenly it crashes with a nil pointer dereference. You panic: "Why? The config should have been loaded first!"
Sound familiar? If so, you're not alone. The question "When does the init() function run?" is one of the most popular on Stack Overflow (thousands of views). Let's finally figure out once and for all when and in what order init() runs in Go, so you never fall into this trap again.
What is init() and why is it needed?
In Go, init() is a special function with no parameters and no return value. It is not called explicitly. It is launched by the Go runtime automatically before the main() function executes.
Why is this needed?
- Initializing global variables that require complex logic (e.g., reading from a file).
- Registering database drivers or HTTP handlers.
- Checking the environment (environment variables, resource availability).
But the main rule: you should never rely on the execution order of init() between different files of the same package. And here's why.
When exactly does init() run? The precise order
Go executes init() according to strict rules. Let's break them down.
Rule #1: One package — one order
Within a single package, init() executes in the order of file declaration (alphabetically by file name). But this is an implementation detail — it's better not to rely on it.
Rule #2: Variable initialization first, then init()
Before calling init(), Go initializes all global variables in the package. If a variable depends on another variable from the same package, their declaration order matters.
package config
var AppName string = "MyApp"var Version string = getVersion() // called before init()
func init() { // AppName and Version are already available here println("Init started for", AppName)}
func getVersion() string { return "1.0.0"}Rule #3: Imported packages are initialized first
This is the most important rule. If package A imports package B, then all init() functions in package B execute first, and only then those in A. Go builds a dependency graph and performs initialization "bottom-up".
// Package databasepackage database
func init() { println("1. database init")}
// Package main (imports database)package main
import "myapp/database"
func init() { println("2. main init")}
func main() { println("3. main function")}
// Output:// 1. database init// 2. main init// 3. main functionRule #4: Multiple init() in one file — possible but not recommended
There can be multiple init() functions in one file. They execute in declaration order. But this makes code harder to read. It's better to combine the logic into one function.
Live example: what happens if you get the order wrong?
Let's simulate a typical beginner mistake. Suppose we have a config package that reads settings from a file, and an app package that uses those settings.
// config.gopackage config
var Settings map[string]string
func init() { // Simulating reading a file Settings = map[string]string{ "port": "8080", "host": "localhost", } println("Config loaded")}
// app.gopackage app
import "myapp/config"
var ServerPort string
func init() { // ERROR: if config.Settings hasn't been initialized yet