Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal: language: Go 2: panic on send/receive on nil channel #21069

Open
ianlancetaylor opened this issue Jul 18, 2017 · 4 comments

Comments

@ianlancetaylor
Copy link
Contributor

commented Jul 18, 2017

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:

var c1 chan int
<-c1

var c2 chan int
select {
case <-c2:
}

var c3 chan int
c3 <- 1

var c4 chan int
select {
case c4 <- 1:
}

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:

https://groups.google.com/d/msg/golang-nuts/QltQ0nd9HvE/mAb2K4UHWxUJ
https://groups.google.com/d/msg/golang-nuts/JFTymqVDBcs/E8GDJiQFBQAJ
http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/index.html#using_nil_ch

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.

@ianlancetaylor ianlancetaylor added this to the Proposal milestone Jul 18, 2017

@bcmills

This comment has been minimized.

Copy link
Member

commented Jul 18, 2017

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.

@carlmjohnson

This comment has been minimized.

Copy link
Contributor

commented Aug 3, 2017

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/

@creker

This comment has been minimized.

Copy link

commented Aug 5, 2017

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.

@toqueteos

This comment has been minimized.

Copy link

commented Aug 18, 2017

This would effectively remove the possibility of "pausing" a channel as explained in Advanced Concurrency Patterns

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
6 participants
You can’t perform that action at this time.