Comments

In Go 1 a send or receive operation on a nil channel blocks forever. This is by analogy with the select statement, in which a case with a nil channel is ignored. In particular, these four code snippets all block forever:

However, the fact is that send statements and receive expressions are not the same as select cases. They look different in the program and they are implemented differently. While it is nice to have a send or receive operation behave identically to a select statement with a single case that corresponds to that operation, it is not necessary, and I claim that changing that correspondence will not cause confusion.

I claim further that the fact that nil channels block forever is a point of confusion for new Go programmers. They must learn to call make to create a channel. When learning Go, it is easy to forget that. This then leads to confusion when programs simply block rather than reporting an error. Sample comments:

My final claim is that essentially no real programs rely on the current behavior for nil channels. I have no way to prove this claim, but in general blocking forever is a goroutine leak, and when people do want to block forever anyhow they use select {}.

Give the above, I recommend that for Go 2 we change this corner case and make send or receive operations on nil channels panic. To be clear I am not proposing that we change the behavior of select statements with nil channels, and I am not proposing that we change any aspect of the behavior for closed channels. In the above four code snippets, the first and third will panic, while the second and fourth will continue to behave as in Go 1.

This comment has been minimized.

To be clear I am not proposing that we change the behavior of select statements with nil channels

I like your proposal and would be happy to see it adopted as-is.

That said, I would go a step further and change the behavior of select statements to match. The use-case for selecting on a nil channel is to toggle whether a particular case can be taken, but it's easy enough to do that with a non-nil channel that has no receivers (to block send cases) or no senders (to block receive cases), and I think the benefit of keeping select and non-select channel ops consistent may outweigh the convenience of nilling out channels to toggle cases.

This comment has been minimized.

I think it's good for the statement to be analogous to the select/case except where it needs to be different, and I find blocking on select/case very useful. A lot of times, I will write a loop with a select inside of it, and being able to "turn off" part of the select block with a nil is very useful. See my blog post about this pattern: https://blog.carlmjohnson.net/post/share-memory-by-communicating/

This comment has been minimized.

How about panic only when sending to a nil channel? That way we match select and receive behavior and keep the feature of select that it ignores nil channels. I can't give an example right now but I remember it being very useful many times. It's also a common pattern for me to write a loop with select in it that has cases I can toggle on and off.

Also, making select panic would break existing Go 1 code. Not only break but require a complex task of redesigning the application to work around the new behavior. Some cases could be simple but, still, no "go fix" could fix that.