Initialization dependencies

March 22, 2024

Here’s a thing you’ve likely never thought about: What if you have a compound assignment statement, where one of the variables depends on the other in the same statement.

That’s what we’re looking at today.

Order of Evaluation

At package level, initialization dependencies override the left-to-right rule for individual initialization expressions, but not for operands within each expression:

var a, b, c = f() + v(), g(), sqr(u()) + v()

func f() int        { return c }
func g() int        { return a }
func sqr(x int) int { return x*x }

// functions u and v are independent of all other variables and functions

So first notice that calling f() returns c, and calling g() returns a. c and a, in turn, are defined in same mult-assignment statement. Such a statement would be evaluated in left-to-right order, but these initialization dependencies make that impossible.

The function calls happen in the order u(), sqr(), v(), f(), v(), and g().

First u() is called, because it has no initialization dependencies, and it’s required to evaluate sqr(). Then sqr() is evaluated and v(), to complete the evaluation of c. This is the right-most assignment in the list, so would normally be executed first, but since c is required to evaluate f(), which is in the left-most assignment, it must happen first.

With that out of the way, we can finally execute f() and v() (again), to complete the assignment to a.

Then g() can be evaluated, to finalize the assignment to b.

Pro tip: If you ever have code like this, just rewrite it to be more obvious and less confusing:

c := sqr(u()) + v()
a := f() + v()
b := g()

Quotes from The Go Programming Language Specification Language version go1.22 (Feb 6, 2024)


Share this

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

Unsure? Browse the archive .

Related Content


Defer execution order

Let’s talk about some of the nuance surrounding defer that’s often lost, or simply forgotten. Defer statements … Each time a “defer” statement executes, the function value and parameters to the call are evaluated as usual and saved anew but the actual function is not invoked. Instead, deferred functions are invoked immediately before the surrounding function returns, in the reverse order they were deferred. That is, if the surrounding function returns through an explicit return statement, deferred functions are executed after any result parameters are set by that return statement but before the function returns to its caller.


Execution of a select statement

I’m looking for a new client or two. Could your team use some expert Go help? Reach out, and let’s talk! Select statements … Execution of a “select” statement proceeds in several steps: For all the cases in the statement, the channel operands of receive operations and the channel and right-hand-side expressions of send statements are evaluated exactly once, in source order, upon entering the “select” statement. The result is a set of channels to receive from or send to, and the corresponding values to send.


Assignment order

We’ve already talked about the order of evaluation in Go. And while that’s perfectly relevant here, it’s only one piece of the puzzle when it comes to ordering with assignment operations: Assignment statements … The assignment proceeds in two phases. First, the operands of index expressions and pointer indirections (including implicit pointer indirections in selectors) on the left and the expressions on the right are all evaluated in the usual order.

Get daily content like this in your inbox!

Subscribe