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
.
a
depends b
, and variables c
and _
depend on a
. So
b
, which is the first package-level variable without dependencies on other package-level variables.
a
. After b
is initialized, a
is the first package-level variable without dependencies on uninitialized package-level variables.
_
and c
. After a
and b
are initialized, _
and c
both don't depend on uninitialized package-level variables.
package main
import "fmt"
var (
_ = f()
a = b / 2
b = 6
c = f()
)
func f() int {
a++
return a
}
func main() {
fmt.Println(a, b, c) // 5 6 5
}
5 6 5
.
var x, y = f()
, variables x
and y
will be initialized together. In other words, no other variables will be initialized between them.
var m, n = expr1, expr2
var m = expr1
var n = expr2
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, and 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} }
a && b
, the right operand expression b
will be evaluated only if the left operand expression a
is evaluated as true
. So b
will be evaluated, if it needs to be evaluated, after the evaluation of a
.
a || b
, the right operand expression b
will be evaluated only if the left operand expression a
is evaluated as false
. So b
will be evaluated, if it needs to be evaluated, after the evaluation of a
.
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()
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()}
c[k]
, then its elementary form is (*cAddr)[k]
, where cAddr
is a pointer pointing to c
.
a
and b
are two addressable variables of the same type, the following assignment
a, b = b, a
// The evaluation phase:
P0 := &a; P1 := &b
R0 := b; R1 := a
// The elementary form: *P0, *P1 = R0, R1
// The carry-out phase:
*P0 = R0
*P1 = R1
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 evaluation phase:
P0 := &i; P1 := &x; T2 := i
R0 := 1; R1 := 2
// Now, T2 == 0
// The elementary form: *P0, (*P1)[T2] = R0, R1
// The carry-out phase:
*P0 = R0
(*P1)[T2] = R1
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 evaluation phase:
P0 := &s; PM1 := &m; K1 := "Go"; P2 := p; PS3 := &s; T3 := 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, (*PS3)[T3] = R0, R1, R2, R3
// The carry-out phase:
*P0 = R0
(*PM1)[K1] = R1
*P2 = R2
(*PS3)[T3] = R3
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]
x := []int{123}
x, x[0] = nil, 456 // will not panic
x, x[0] = []int{123}, 789 // will panic
x+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)
}
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 Blocks
switch-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 in 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):
}
}
f()
calls will be evaluated by the order from top to bottom and from left to right, until a comparison results in 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
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).
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
}
}
bbb ddd eee fff ggg hhh ccc
*fptr("ccc")
is the last evaluated expression in the above example. It is evaluated after its corresponding receive operation <-fchan("ddd", c)
is selected.
The Go 101 project is hosted on Github. Welcome to improve Go 101 articles by submitting corrections for all kinds of mistakes, such as typos, grammar errors, wording inaccuracies, description flaws, code bugs and broken links.
If you would like to learn some Go details and facts every serveral days, please follow Go 101's official Twitter account @zigo_101.
reflect
standard package.sync
standard package.sync/atomic
standard package.