This article will explain expression evaluation orders in all kinds of scenarios.
f(x, y[n])
,
f()
is evaluated later than its depended expressions,
including f
, x
and y[n]
.
y[n]
is later than
the evaluations of n
and y
.
Please read program resource initialization order for another example on package-level variable initialization orders.
When a package is loaded at run time, Go runtime will try to initialize uninitialized package-level variables which have no dependencies on uninitialized variables, by their declaration order. The process might be applied (looped) several times, until no variables are initialized in such a process. For a successfully compiled Go program, there should be no uninitialized variables after all such processes end.
Package-level variables appearing as blank identifers are treated like any other variables in the initialization process.
For example, the following program should printyzxw
.
y
and z
are the only two uninitialized variables which have no dependencies on
uninitialized variables, so they are initialized by their declaration order.
x
is the only uninitialized variable which has no dependencies
on uninitialized variables, so it is initialized.
w
is the only uninitialized variable which has no dependencies
on uninitialized variables, so it is initialized.
package main
var (
_ = f("w", x)
x = f("x", z)
y = f("y")
z = f("z")
)
func f(s string, deps ...int) int {
print(s)
return 0
}
func main() {
f("\n")
}
Note, currently (Go 1.12), the standard Go compiler
doesn't implement the rule correctly.
If the above program is compiled with the standard Go compiler, it will print zxwy
.
Multiple variables on the left-hand side of a variable declaration
initialized by single multi-valued expression on the right-hand side
are initialized together. For example, for a package-level variable
declaration var x, y = f()
, variables x
and y
will be initialized together. In other words, no other variables will
be initialized between them.
a
will be initialized after b
for sure,
x
is initialized before b
, between
b
and a
, or after a
, is not specified.
sideEffect()
is called (before
or after x
is initialized) is also not specified.
// x has a hidden dependency on a and b
var x = I(T{}).ab()
// Assume sideEffect is unrelated to x, a, or b.
var _ = sideEffect()
var a = b
var b = 42
type I interface { ab() []int }
type T struct{}
func (T) ab() []int { return []int{a, b} }
The just described order is called the usual order.
Please note that an explicit value conversion T(v)
is not a function call.
[]int{x, fa(), fb(), y}
,
assume x
and y
are two variables,
fa
and fb
are two functions, then the call fa()
is guaranteed to be evaluated (executed) before fb()
.
However, the following the evaluation orders are unspecified in Go specification:
x
(or y
) and fa()
(or fb()
).
x
, y
, fa
and fb
.
y[z.f()], ok = g(h(a, b), i()+x[j()], <-c), k()
where
c
is a channel expression and will be evaluated to a channel value.
g
, h
, i
, j
and k
are function expressions.
f
is a method of expression z
.
z.f()
→h()
→i()
→j()
→<-c
→g()
→k()
.
h()
is evaluated after the evaluations of expressions h
, a
and b
.
y[]
is evaluated after the evaluation of z.f()
.
z.f()
is evaluated after the evaluation of expression z
.
x[]
is evaluated after the evaluation of j()
.
y
, z
, g
,
h
, a
, b
, x
,
i
, j
, c
and k
.
y[]
, x[]
and <-c
.
x
,
m
and n
(also demoed in Go specification)
will be initialized with ambiguous values.
a := 1
f := func() int { a++; return a }
// x may be [1, 2] or [2, 2]: evaluation order
// between a and f() is not specified.
x := []int{a, f()}
// m may be {2: 1} or {2: 2}: evaluation order
// between the two map element assignments is
// not specified.
m := map[int]int{a: 1, a: 2}
// n may be {2: 3} or {3: 3}: evaluation order
// between the key and the value is unspecified.
n := map[int]int{a: f()}
Later, we call the first phase as evaluation phase and the second phase as carry-out phase.
Go specification doesn't specify clearly how the operands in the mentioned expressions on the left should be exactly evaluated, which ever caused some disputes. So, here, this article will explain more on the evaluation orders in value assignments.
To make the following explanations convenient, we assume that the map value of a map index destination expression in an assignment is always addressable. If it is not, we can think the map value has already been saved in and replaced by a temporary addressable map value before executing the assignment.
At the time of the end of the evaluation phase and just before the carry-out phase starts, each destination expression on the left of an assignment has been evaluated as its elementary form. Different destination expressions have different elementary forms.m[k]
,
then its elementary form is (*mAddr)[k]
,
where mAddr
is a pointer pointing to m
.
a
and b
are two addressable variables of the same type, the following assignment
a, b = b, a
will be executed like the following steps:
// The evaluation phase:
P0 := &a; P1 := &b
R0 := a; R1 := b
// The elementary form: *P0, *P1 = R0, R1
// The carry-out phase:
*P0 = R0
*P1 = R1
Here is another example, in which
x[0]
instead of x[1]
is modified.
x := []int{0, 0}
i := 0
i, x[i] = 1, 2
fmt.Println(x) // [2 0]
The decomposed execution steps for the line 3 shown below are like:
// The evaluation phase:
P0 := &i; P1 := &x[0];
R0 := 1; R1 := 2
// The elementary form: *P0, *P1 = R0, R1
// The carry-out phase:
*P0 = R0
*P1 = R1
An example which is a little more complex.
package main
import "fmt"
func main() {
m := map[string]int{"Go": 0}
s := []int{1, 1, 1}; olds := s
n := 2
p := &n
s, m["Go"], *p, s[n] = []int{2, 2, 2}, s[1], m["Go"], 5
fmt.Println(m, s, n) // map[Go:1] [2 2 2] 0
fmt.Println(olds) // [1 1 5]
}
The decomposed execution steps for the line 10 shown below are like:
// The evaluation phase:
P0 := &s; PM1 := &m; K1 := "Go"; P2 := p; P3 := &s[2]
R0 := []int{2, 2, 2}; R1 := s[1]; R2 := m["Go"]; R3 := 5
// now, R1 == 1, R2 == 0
// The elementary form:
// *P0, (*PM1)[K1], *P2, *P3 = R0, R1, R2, R3
// The carry-out phase:
*P0 = R0
(*PM1)[K1] = R1
*P2 = R2
*P3 = R3
The following example rotates all elements in a slice for one index.
x := []int{2, 3, 5, 7, 11}
t := x[0]
var i int
for i, x[i] = range x {}
x[i] = t
fmt.Println(x) // [3 5 7 11 2]
Another example:
x := []int{123}
x, x[0] = nil, 456 // will not panic
x, x[0] = []int{123}, 789 // will panic
Although it is legal, it is not recommended to use complex multi-value assignments in Go, for their readabilities are not good and they have negative effects on both compilation speed and execution performance.
As mentioned above, not all orders are specified in Go specification for value assignments, so some bad written code may produce different results. In the following example, the expression order ofx+1
and f(&x)
is not specified.
So the example may print 100 99
or 1 99
.
package main
import "fmt"
func main() {
f := func (p *int) int {
*p = 99
return *p
}
x := 0
y, z := x+1, f(&x)
fmt.Println(y, z)
}
The following is another example which will print ambiguous results. It may print
1 7 2
, 1 8 2
or 1 9 2
,
depending on different compiler implementations.
package main
import "fmt"
func main() {
x, y := 0, 7
f := func() int {
x++
y++
return x
}
fmt.Println(f(), y, f())
}
switch-case
Code Blocksswitch-case
code block has been
described before.
Here just shows an example.
Simply speaking, before a branch code block is entered, the case
expressions
will be evaluated and compared with the switch expression one by one,
until a comparison results true
.
package main
import "fmt"
func main() {
f := func(n int) int {
fmt.Printf("f(%v) is called.\n", n)
return n
}
switch x := f(3); x + f(4) {
default:
case f(5):
case f(6), f(7), f(8):
case f(9), f(10):
}
}
At run time, the f()
calls will be evaluated by the order
from top to bottom and from left to right,
until a comparison results true
.
So f(8)
, f(9)
and f(10)
will be not evaluated in this example.
f(3) is called.
f(4) is called.
f(5) is called.
f(6) is called.
f(7) is called.
select-case
Code Blocks
When executing a select-case
code block, before entering a branch code block,
all the channel operands of receive operations and the operands of send statements
involved in the select-case
code block are evaluated exactly once,
in source order (from top to bottom, from left to right).
Note, the target expression being assigned to by a receive case
operation will only be evaluated if that receive operation is selected later.
*fptr("aaa")
will never get evaluated,
for its corresponding receive operation fchan("bbb", nil)
will not be selected.
package main
import "fmt"
func main() {
c := make(chan int, 1)
c <- 0
fchan := func(info string, c chan int) chan int {
fmt.Println(info)
return c
}
fptr := func(info string) *int {
fmt.Println(info)
return new(int)
}
select {
case *fptr("aaa") = <-fchan("bbb", nil): // blocking
case *fptr("ccc") = <-fchan("ddd", c): // non-blocking
case fchan("eee", nil) <- *fptr("fff"): // blocking
case fchan("ggg", nil) <- *fptr("hhh"): // blocking
}
}
The output of the above program:
bbb
ddd
eee
fff
ggg
hhh
ccc
Note that the expression *fptr("ccc")
is the last evaluated expression in the above example.
It is evaluated after its corresponding receive operation <-fchan("ddd", c)
is selected.