-
Notifications
You must be signed in to change notification settings - Fork 426
An ambiguity of (or dispute on) the evaluation order of LHS (left hand side) items in a multi value assignment
Let's view several simple Go code examples firstly.
The first one:
package main
import "fmt"
func main() {
a := []int{1, 2}
a, a[0] = []int{5, 6}, 3
fmt.Println(a)
}
What do you expect the above program will print? [5 6]
or [3 6]
?
FYI, gc (the standard Go compiler) prints [5 6]
, however gccgo prints [3 6]
.
The second example:
package main
import "fmt"
func main() {
arr := []int{1, 2}
arr, arr[len(arr)-1] = arr[:len(arr)-1], 3
fmt.Println(arr)
}
Will the second program panic or not?
FYI, gc prints [1]
without panicking, but gccgo panics by thinking "index out of range" happens in arr[len(arr)-1]
.
There is a similar example to the second one.
The third example:
package main
import "fmt"
func main() {
var s []int
s, s[0] = []int{1, 2, 3}, 9
fmt.Println(s)
}
Unsurprisingly, gc panics for the third program, and gccgo prints [9 2 3]
without panicking.
Ok, let's talk the main topic. Is the reason of the inconsistencies between gc and gccgo for above programs same as the reason for the inconsistencies for the examples in the article some evaluation orders in multi value assignments are unspecified?
The answer is partly yes. Whereas all Go core team members agree that the inconsistencies for the examples in the article some evaluation orders in multi value assignments are unspecified are caused by some evaluation orders are unspecified, some Go core team members think gccgo does it wrongly for the examples mentioned above. In other words, they think the rules are well defined here. But gccgo maintainer Ian Lance Taylor doesn't think so. (update: Ian accepted the opinion of the other in the end.)
The core of the dispute is whether or not the slice index items on the left side of an assignment should be evaluated to the element level or the container level. For example, the dispute is which one of the following two step flows should be adopted for the third example.
Step flow one (adopted by gc):
// s, s[0] = []int{1, 2, 3}, 9
x := s
s = []int{1, 2, 3}
x[0] = 9
Step flow two (adopted by gccgo):
// s, s[0] = []int{1, 2, 3}, 9
s = []int{1, 2, 3}
s[0] = 9
Personally, I prefer the gc
interpretation. However, maybe Go specification is really a little ambiguous here:
... 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.
Nothing more to say. Just an advice for Go programming in practice, don't use multi-value assignments like the ones in this article. Split each of such multi-value assignments into multiple single-value assignments instead.
Finally, another reason we should try not to use multi-value assignments in Go.
The following program should print 1
for len(m)
, but the current gc (v1.10.3) prints it as 0
instead
(gccgo does it right). This is a known bug of gc.
package main
import "fmt"
func main() {
var m = map[int]int{}
var p *int
defer func() {
fmt.Println(recover())
fmt.Println(len(m)) // 0
}()
m[2], *p = 1, 2
}
[Update]: Ian accepted the opinion of the other in the end. So this dispute should be cleared since about gccgo 9.
Please follow the official Twitter account of Go 101, @zigo_101, to learn some Go details, facts, tips, etc, and read some Go articles from time to time.
Go 101 is a series of books about Go programming.
Books in Go 101 series
- Go (Fundamentals) 101 focuses on Go syntax/semantics (except custom generics related) and all kinds of runtime related things.
- Go Optimizations 101 provides some code performance optimization tricks, tips, and suggestions.
- Go Details & Tips 101 collects many details and provides several tips in Go programming.
- Go Generics 101 explains Go custom generics in detail.
Tapir started writing the Go 101 books and maintaining the go101.org website since 2016. New contents will continue being added to the books and the website from time to time. If you would like to, you can also support Go 101 by playing Tapir's games (for both Android and iPhone/iPad):
- Color Infection (★★★★★) - a physics based casual puzzle original game. 140+ levels.
- Rectangle Pushers (★★★★★) - a casual puzzle original game. 104+ levels.
- Let's Play With Particles - a casual action original game. Three game modes are included.