How does a non initialized channel behave?
I have a struct that holds a channel that is not initialized.
When I write into it, the routine is blocking as expected but the reader is never notified that something is in the pipe.
I am surprised that there is no error and I am wondering what is doing Go.
In the example bellow, neither the message pushed
nor got it
are printed. (Uncomment the intialization and it will work like a charm)
type MyStruct struct {
over chan bool
}
func main() {
nonInitialized := &MyStruct{}
// nonInitialized.over = make(chan bool)
go func() {
for i := 0; i < 10; i++ {
select {
case <-nonInitialized.over:
fmt.Println("got it")
default:
// proceed
}
fmt.Println("do some stuff")
time.Sleep(10 * time.Millisecond)
}
panic("took too long")
}()
// push on the non initialized channel
fmt.Println("pushing")
nonInitialized.over <- true
fmt.Println("pushed")
}
Here is the playground https://play.golang.org/p/76zrCuoeoh
(I know I should initialize the channel, this is not the purpose of the question, I want to know what is happening in Go with non initialized channels.)
Solution 1:
An "uninitialized" field or variable of channel type will have the zero value of all channel types which is nil
. So let's examine how a nil
channel or operations on it behave.
It is worth collecting the channel axioms in one post:
- a send on a
nil
channel blocks forever (Spec: Send statements) - a receive from a
nil
channel blocks forever (Spec: Receive operator) - a send to a closed channel panics (Spec: Send statements)
- a receive from a closed channel returns the zero value immediately (Spec: Receive operator)
Reasoning for blocking in case of nil
channels: if a channel value is nil
, no one has a reference to it, so no one will ever be ready to receive from it (what we want to send); or send anything on it (what we would receive from it).
You can read further reasoning and more details about this in Dave Cheney: Channel Axioms.
For completeness:
-
Closing a
nil
channel causes a run-time panic (just like closing an already closed channel). -
Length and capacity of a
nil
channel is0
; in accordance withnil
slices and maps having0
length and capacity.
Reasoning: "closed" is a state, but a nil
channel cannot have a state (there is only one nil
channel, and not one for "closed" and one for "not closed" channel). And there are no elements queued in a nil
channel (so len = 0), and it does not have a buffer capacity (so cap = 0).
Solution 2:
This is expected behavior. A send to a nil
channel blocks forever. You can read about it in the specs here: https://golang.org/ref/spec#Send_statements
The same is also applicable for a recieve on a nil
channel. (https://golang.org/ref/spec#Receive_operator)
You can also keep this link handy for reference: http://dave.cheney.net/2014/03/19/channel-axioms