For statements with
range
clause…
- The iteration order over maps is not specified and is not guaranteed to be the same from one iteration to the next. If a map entry that has not yet been reached is removed during iteration, the corresponding iteration value will not be produced. If a map entry is created during iteration, that entry may be produced during the iteration or may be skipped. The choice may vary for each entry created and from one iteration to the next. If the map is
nil
, the number of iterations is 0.
The idea that map porder is non-deterministic in Go is simple, but often forgotten or overlooked. It has three distinct implications, which are spelled out. Let’s go through each one:
-
When ordering over the same map multiple times, you may get a different order of results.
for k, v := range map[string]string{"cow": "moo", "pig": "oink"} { fmt.Println(k, v) }
may produce:
cow moo pig oink
or
pig oink cow moo
-
Keys deleted while ranging won’t be visited
… however this can result in some non-deterministic behavior in some cases. Consider:
m := map[string]string{"cow": "moo", "pig": "oink"} for k, v := range m { fmt.Println(k, v) delete(m, "cow") }
After running this code,
m
will always equalmap[string]string{"pig": "oink"}
, but depending on the order of the iteration, the output may be eithercow moo pig oink
or
pig oink
-
Keys inserted into a map while ranging, may or may not be visited during iteration.
m := map[string]string{"cow": "moo"} for k, v := range m { fmt.Println(k, v) m["chicken"] = "cluck" }
will set
m
to equalmap[string]string{"cow": "moo", "chicken": "cluck"}
, but the output may be either:cow moo
or
cow moo chicken cluck
All this to say, it’s important to be intentional when ranging over maps. The results are unpredictable!
Quotes from The Go Programming Language Specification Language version go1.22 (Feb 6, 2024)