How does initialization work across packages, and what are the rules for init()?
Question 21MediumGo 1.22 to 1.25
Order of initialization:
- Imported packages are initialized first, depth-first. Each package is initialized exactly once, no matter how many packages import it.
- Inside a package, all package-level variables are initialized in dependency order.
- Then every
init()function runs, file by file, in the order they appear. - Finally,
main.mainruns.
Since Go 1.21, the order among packages that could run at the same point is fully defined: they are sorted by import path.
init rules:
- It takes no arguments and returns nothing.
- One package, or even one file, can have many
initfunctions. - It can't be called or referenced (
init()in your code gives "undefined: init"). - Everything runs in one goroutine, but
initcan start goroutines.
import (
"database/sql" // used elsewhere: sql.Open("postgres", dsn)
_ "github.com/lib/pq" // blank import: only runs pq's init(), which calls sql.Register
_ "net/http/pprof" // registers /debug/pprof handlers on DefaultServeMux
)
What the interviewer wants to hear: init is fine for registration. It should not be used for I/O, config parsing or anything that can fail. Hidden global state makes testing harder and slows startup. Prefer explicit constructors. Also, flag.Parse() belongs in main, not in init. Since Go 1.13, test flags are registered only when tests start, so parsing in init breaks go test.
More on Language Fundamentals & Types
- Q19Do deferred functions run on
os.Exit,log.Fatal,runtime.Goexitandpanic? - Q20In what order are package-level variables initialized? What does this print?
- Q22How do labeled
breakandcontinuework? Why does a plainbreakinsideselectnot leave the loop? - Q23What restrictions does Go place on
goto? - Q24How does
switchdiffer from C, and what does thisfallthroughexample print? - Q25Which types are comparable in Go, and when does comparing values panic at run time?