Package initialization order: what does this program print?
package main
import "fmt"
var (
a = c + b
b = f("b")
c = f("c")
d = f("d")
)
func f(s string) int {
fmt.Print(s, " ")
return len(s)
}
func init() { fmt.Print("init1 ") }
func init() { fmt.Print("init2 ") }
func main() {
fmt.Println("main", a, d)
}
Output: b c d init1 init2 main 2 1.
Package-level variables are initialized by dependency, not simply top to bottom. The spec repeatedly picks the earliest variable, in declaration order, whose dependencies are all initialized. a depends on c and b, so it waits. b is ready and runs first, then c (the operand order inside c + b does not matter). Then a becomes ready and gets 1 + 1 = 2, and after that d runs. A dependency cycle between package variables is a compile error (initialization cycle).
All variables are initialized before any init function runs. A package may have several init functions, even in one file. They run in the order they appear, with files presented to the compiler in file-name order. Every imported package is fully initialized first, which is why fmt.Print works inside f. Since Go 1.21 the order across packages is precisely defined as well (sorted by import path, with dependencies first).
Code-review angle: init functions that read files, open connections, or rely on another package's init side effects make code hard to test and cause surprising order bugs. Prefer explicit constructors, or sync.OnceValue for lazy globals.
More on Tricky Output & Code-Review Puzzles
- Q513Embedding is not inheritance: what does this print, and which line does not compile?
- Q514Type switch puzzle: what does describe return for each input?