When package variable initialization order isn't defined

November 15, 2024

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 after b but whether x is initialized before b, between b and a, or after a, and thus also the moment at which sideEffect() is called (before or after x 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)


Share this

Direct to your inbox, daily. I respect your privacy .

Unsure? Browse the archive .

Related Content


init functions

We’ve been looking at the order of package variable initialization. What if these rules are confusing? Or even non-desterministic for you? Or maybe you simply want to do something more advanced than is feasible in package variable declarations. Is there an alternative? There is! Package initialization … Variables may also be initialized using functions named init declared in the package block, with no arguments and no result parameters. func init() { … } Note that package variables can be initialized, but not declared within an init function.


Package variable dependency analysis

Today’s section of the spec is a bit dense and technical. But don’t worry, it makes a lot of sense once we get to the end. Package initialization … Dependency analysis does not rely on the actual values of the variables, only on lexical references to them in the source, analyzed transitively. For instance, if a variable x’s initialization expression refers to a function whose body refers to variable y then x depends on y.


Variable declaration across files

Package initialization … The declaration order of variables declared in multiple files is determined by the order in which the files are presented to the compiler: Variables declared in the first file are declared before any of the variables declared in the second file, and so on. To ensure reproducible initialization behavior, build systems are encouraged to present multiple files belonging to the same package in lexical file name order to a compiler.

Get daily content like this in your inbox!

Subscribe