We’re nearing the end of the discussion on package initialization order. Monday should be the last day on that topic, but more on that shortly.
Up to now, we’ve been looking at the deterministic order-of-initialization rules.Today’s topic is when that order is not defined.
Package initialization
…
Dependency analysis is performed per package; only references referring to variables, functions, and (non-interface) methods declared in the current package are considered. If other, hidden, data dependencies exists between variables, the initialization order between those variables is unspecified.
For instance, given the declarations
var x = I(T{}).ab() // x has an undetected, hidden dependency on a and b var _ = sideEffect() // unrelated to x, a, or b var a = b var b = 42 type I interface { ab() []int } type T struct{} func (T) ab() []int { return []int{a, b} }
the variable
a
will be initialized afterb
but whetherx
is initialized beforeb
, betweenb
anda
, or aftera
, and thus also the moment at whichsideEffect()
is called (before or afterx
is initialized) is not specified.
Although mentioned in the text, the example doesn’t show what might be a more common issue: cross-package references. Consider something like this:
package mypkg
var foo = io.EOF
var bar = func() int {
fmt.Println(eof)
fmt.Println(io.EOF)
return i
}()
In practice, I see this outputting:
EOF
EOF
But according to the spec, it could also output:
<nil>
EOF
And that’s because the initialization of foo
, which references another package, is not guaranteed to happen in any particular order.
But what if you need a deterministic order for some package variable that references another package? That’s what we’ll talk about next time.
Quotes from The Go Programming Language Specification Language version go1.23 (June 13, 2024)