Modifying a map while ranging over it

4,907 views
Skip to first unread message

Joshua Liebow-Feeser

unread,
Feb 16, 2014, 6:04:51 PM2/16/14
to golan...@googlegroups.com
I know that adding or deleting elements to or from a map while ranging over it can cause unspecified behavior. However, is the same true of only modifying elements? So, for example:

for k, v := range myMap {
    myMap[k] = getNewValue()
}

Andrew Gerrand

unread,
Feb 16, 2014, 6:08:11 PM2/16/14
to Joshua Liebow-Feeser, golang-nuts
This is fine and will work as expected. (except it won't compile because you're not using v ;-)

Caleb Spare

unread,
Feb 16, 2014, 6:08:53 PM2/16/14
to Joshua Liebow-Feeser, golang-nuts
On Sun, Feb 16, 2014 at 3:04 PM, Joshua Liebow-Feeser <josh...@gmail.com> wrote:
I know that adding or deleting elements to or from a map while ranging over it can cause unspecified behavior.

The behavior is quite well-specified:

If map entries that have not yet been reached are removed during iteration, the corresponding iteration values will not be produced. If map entries are created during iteration, that entry may be produced during the iteration or may be skipped.
 
However, is the same true of only modifying elements? So, for example:

for k, v := range myMap {
    myMap[k] = getNewValue()

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Joshua Liebow-Feeser

unread,
Feb 16, 2014, 6:35:31 PM2/16/14
to golan...@googlegroups.com, Joshua Liebow-Feeser
Ok, great; thanks! (and good point ;)

Joshua Liebow-Feeser

unread,
Feb 16, 2014, 6:36:21 PM2/16/14
to golan...@googlegroups.com, Joshua Liebow-Feeser
That's interesting. I remember it at some point being unspecified. I could be simply misremembering, but was this specification added post-go1?

Bill DeRose

unread,
Feb 16, 2014, 6:46:14 PM2/16/14
to golan...@googlegroups.com, Joshua Liebow-Feeser
This behavior is especially important when sharing maps between goroutines. That is, if goroutine 1 is ranging over a map and goroutine 2 deletes some element from the map, then if the range in goroutine 1 has not reached the deleted element it will not be iterated over in goroutine 1.

The question at hand, I think, is asking about altering the contents of the map in the same goroutine. As Andrew said, it won't result in any unexpected behavior. Adding elements to maps also has some interesting consequences. From the spec:
"If map entries are 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."

Jesse McNelis

unread,
Feb 16, 2014, 6:54:21 PM2/16/14
to Bill DeRose, golang-nuts, Joshua Liebow-Feeser
On Mon, Feb 17, 2014 at 10:46 AM, Bill DeRose <bill.d...@gmail.com> wrote:
This behavior is especially important when sharing maps between goroutines. That is, if goroutine 1 is ranging over a map and goroutine 2 deletes some element from the map, then if the range in goroutine 1 has not reached the deleted element it will not be iterated over in goroutine 1.

That behaviour is the unspecified behaviour. 
If you're modifying a map while iterating over it in another goroutine the behaviour of the entire program from then on is undefined.
See http://golang.org/ref/mem for details.

Joshua Liebow-Feeser

unread,
Feb 16, 2014, 6:55:46 PM2/16/14
to golan...@googlegroups.com, Joshua Liebow-Feeser
Is there really a scenario in which you'd be ranging over the same map in two different goroutines at the same time while modifying it? Maps are not safe for concurrent modification, so the only way I could imagine that being safe would be something like:

goroutine 1:

for k, v := range myMap {
mtx.Lock()
mutate(myMap, k, v)
mtx.Unlock()
}

goroutine 2:

for k, v := range myMap {
mtx.Lock()
mutate(myMap, k, v)
mtx.Unlock()
}

Note that there are two subtle variations on this example. First, you could simply lock for the entire for loop, but then it wouldn't actually be concurrent access. Second, you could avoid mutations, and only do reads on the map, in which case having behavior under mutation be well-defined isn't necessary since there isn't any mutation happening in the first place.
Reply all
Reply to author
Forward
0 new messages