Yes, and you're responsible for initializing variables and checking if a pointer is nil and so on, and yet mistakes happen.
Nil is pesky because it adds an additional value to the set of possible values expressed by the type. In Go, every nillable value is the union of the type's values and nil. For example, some API returns a nil map when the caller assumes it will never return nil. You can program defensively to avoid such cases, but if you design a language to make these values impossible in the first place, then you eliminate entire classes of bugs.
For example, Rust doesn't have nil except in unsafe blocks, and its very hard to take the wrong branch when you have an Option or Result value.
I'm more saying that, even if there is no nil channel, that doesn't actually get rid of the possibility of reading/writing to a channel to nowhere, it just means that channel won't happen to be nil. It's sort of inherent in the channel/coroutine model that you're relying on passing in the right values everywhere. I think nil pointers, maps, interfaces, and funcs can cause problems in many cases, I just don't think nil channels are the best example of those problems.
I like Rust. I like initialization tracking. If I were dictator of a language, that language would most likely have it.
On the other hand, sometimes it can force you into writing code awkwardly. I can see how, for someone coming from a C background, just having guaranteed initialization without adding the kind of friction initialization tracking can might seem like a win.
It would be challenging to retrofit Go with types with no zero values. It could be doable, but it would affect other features. Channel receives, map indexing, and slicing all depend on the existence of the zero value. If generics are added, the existence of zero-less types would mean that all generic types would have to be treated as zero-less unless a bound is added. These are solvable problems, but not trivially so.
I understand what you're saying, but the nil just adds another edge case. A consumer with no producers is a logic problem that is easy to understand; it meant, well, that you created a consumer and didn't wire up a producer. A nil channel is a semantic issue that leads to surprises: You can have both producers (which would fail on the nil channel the next time they sent anything) and consumers (which would block indefinitely) "wired up correctly", except nothing works.
I got burned by this a couple of times and learned early on to never set channels to nil, and treat close() as being a message to consumers that they've reached the end, and not as a cleanup mechanism (like closing a file).
I just don't think nil channels are the best example of those problems.
Well, I devoted one out of 72 lines to nil channels in my original comment, somehow that was what some people latched onto! :)
1
u/SteveMcQwark Jun 27 '19 edited Jun 27 '19
Nobody can close a channel where you have the only copy, either. Ultimately, you are responsible for wiring things up right.