Repository: go
Issue: spec: clarify range over maps will produce every non-deleted entry avaiable when it starts
Labels: Documentation,NeedsDecision

### What version of Go are you using (`go version`)?

N/A, but 1.13

### Does this issue reproduce with the latest release?

Sure.

### What operating system and processor architecture are you using (`go env`)?

N/A

### What did you do?

https://play.golang.org/p/jX-Bp63JDHX

### What did you expect to see?

I have no idea. This is a spec question, really. I'm writing it because I brought this up in a couple of places and ended up with a significant range of intuitions and answers from experienced and competent developers, ranging from "that will work" to "that won't work" to "that might work". I think it should perhaps be clearer whether or not this is "safe" -- could this code produce a race condition or concurrency failure?

If you have a range loop on a map, and unsynchronized writes to the map, that's a clear violation -- you can't read the map while it's being written to.

But let's say, hypothetically, that you want to range over a map slowly -- say, for a low priority background cleanup task. You don't want to run the entire thing with a lock held preventing the map from being updated. You could, of course, lock the map, grab a slice of keys, unlock it, and iterate the keys.

But why would you do something sane like that when you can do something *insane*?

Enter the example code: Grab a lock, start a range loop, and then inside the body of the loop, release the lock, operate on the k/v pair, and grab the lock again right before resuming at the range statement. Each evaluation of the range statement happens with the lock held. If the same lock controls modifications to the map, that's... maybe... safe?

I can't tell whether it's safe. I think it probably is, not because the spec says so, but because the spec says something that implies it, I think:

> 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. 

This suggests that map entries may be removed or created during iteration, which implies that the range statement is expected to function at least somewhat if there's creation or deletion while it's running. It's less obvious whether the creation or deletion will be reliable. Imagine for instance that we have a map with a hundred items, we iterate through 90 of them, and then we add a thousand more items, causing the entire map to get grown and rehashed at least once. Do I reliably expect to see the other 10 items that were in it before I started? I have no idea. It looks to me like the current implementation is at least *trying* to handle that case carefully, and indeed, I can't make it fail; I always hit every item that was present when the range statement started and hasn't been deleted yet, but don't necessarily hit any new items.

I can't tell whether I'm safe or (un?)lucky.