I was under the impression that despite the differences in syntax, function a and function b below were logically equivalent. However, they are not and I do not understand t
The Go Programming Language Specification
Assignments
the number of operands on the left must equal the number of expressions on the right, each of which must be single-valued, and the nth expression on the right is assigned to the nth operand on the left:
one, two, three = '一', '二', '三'
The blank identifier provides a way to ignore right-hand side values in an assignment:
_ = x // evaluate x but ignore it x, _ = f() // evaluate f() but ignore second result value
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. Second, the assignments are carried out in left-to-right order.
Tuple assignments are two phase assignment. 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. Second, the assignments are carried out in left-to-right order.
For example,
package main
import "fmt"
func a() (int, int, int) {
x := 1
y := 2
z := 3
// phase 1
tx := x
ty := y
// phase 2
z = tx
x = ty
y = tx + ty
return x, y, z
}
func b() (int, int, int) {
x := 1
y := 2
z := 3
z, x, y = x, y, x+y
return x, y, z
}
func main() {
fmt.Println(a())
fmt.Println(b())
}
Output:
2 3 1
2 3 1
The simple answer is because it's all one statement and the value of y
hasn't been updated to 2
at the point when x+y
is evaluated. IE the expression on the right hand side is evaluated prior to any assignment. In the other case everything happens on step at a time so ofc y
's value has been updated to 2
and you get four.
Interesting problem for academic purposes, terrible code in real life so please don't write anything like that in a real program.
Assignment can be thought of as an "atomic" operation. That is, it's useful to think that all values on the left hand side of the =
are "frozen" until all of the operations are finished.
Consider the following program:
package main
import "fmt"
func swap() (int, int) {
x := 1
y := 2
x, y = y, x
return x, y
}
func main() {
fmt.Println(swap()) // prints 2 1
}
Without this "freezing" behaviour, you would get 2
for both x
and y
, which is probably not what you'd expect from the code. It's also probably easier to reason about the semantics of this "freezing" behaviour than if the "cascading" approach were taken.