32

Why does Go panic on writing to a closed channel?

While one can use the value, ok := <-channel idiom for reading from channels, and thus the ok result can be tested for hitting a closed channel:

// reading from closed channel

package main

import "fmt"

func main() {
    ch := make(chan int, 1)
    ch <- 2
    close(ch)

    read(ch)
    read(ch)
    read(ch)
}

func read(ch <-chan int) {
    i,ok := <- ch   
    if !ok {
        fmt.Printf("channel is closed\n")
        return
    }
    fmt.Printf("read %d from channel\n", i)
}

Output:

read 2 from channel
channel is closed
channel is closed

Run "reading from closed channel" on Playground

Writing to a possibly closed channel is more convoluted, because Go will panic if you simply try to write when the channel is closed:

//writing to closed channel

package main

import (
    "fmt"
)

func main() {
    output := make(chan int, 1) // create channel
    write(output, 2)
    close(output) // close channel
    write(output, 3)
    write(output, 4)
}

// how to write on possibly closed channel
func write(out chan int, i int) (err error) {

    defer func() {
        // recover from panic caused by writing to a closed channel
        if r := recover(); r != nil {
            err = fmt.Errorf("%v", r)
            fmt.Printf("write: error writing %d on channel: %v\n", i, err)
            return
        }

        fmt.Printf("write: wrote %d on channel\n", i)
    }()

    out <- i // write on possibly closed channel

    return err
}

Output:

write: wrote 2 on channel
write: error writing 3 on channel: send on closed channel
write: error writing 4 on channel: send on closed channel

Run "writing to closed channel" on Playground

As far as I know, there is not a simpler idiom for writing into a possibly closed channel without panicking. Why not? What is the reasoning behind such an asymmetric behavior between read and write?

Everton
  • 12,589
  • 9
  • 47
  • 59
  • 3
    How would we know? Ask on google golang group, maybe one of the authors will answer you. I can think of one reason. It just a good design to close a channel on producer side. Panicking forces you to design your application in such a way. – creker Jan 20 '16 at 10:50
  • 6
    Closing a channel is a signal that here will be no more values. Writing to a closed channel is a program error, which panics. – JimB Jan 20 '16 at 11:32

2 Answers2

33

From the Go Language Spec:

For a channel c, the built-in function close(c) records that no more values will be sent on the channel. It is an error if c is a receive-only channel. Sending to or closing a closed channel causes a run-time panic. Closing the nil channel also causes a run-time panic. After calling close, and after any previously sent values have been received, receive operations will return the zero value for the channel's type without blocking. The multi-valued receive operation returns a received value along with an indication of whether the channel is closed.

If you write to a closed channel, your program will panic. You could potentially catch this error with recover if you really want to do that, but being in a situation where you don't know whether the channel you are writing to is open is usually a sign of a bug in the program.

Some quotes:

Here is a motivation:

A channel "close" is really just a send of a special value on a channel. It is a special value that promises that no more values will be sent. Attempting to send a value on a channel after it has been closed will panic, since actually sending the value would violate the guarantee provided by close. Since a close is just a special kind of send, it is also not permitted after the channel has been closed.

Here is another:

The only use of channel close is to signal to the reader that there are no more values to come. That only makes sense when there is a single source of values, or when multiple sources coordinate. There is no reasonable program in which multiple goroutines close a channel without communicating. That would imply that multiple goroutines would know that there are no more values to send--how could they determine that if they don't communicate?

(Ian Lance Taylor)

--

Here is another:

Closing a channel releases it as a resource. It makes no more sense to close a channel multiple times than it makes to close a file descriptor multiple times, or free a block of allocated memory multiple times. Such actions imply the code is broken, which is why closing a closed channel triggers a panic.

(Rob Pike)

--

Source: Go design detail rationale question - channel close

Everton
  • 12,589
  • 9
  • 47
  • 59
Justlike
  • 1,476
  • 10
  • 18
  • "but being in a situation where you don't know whether the channel you are writing to is open is usually a sign of a bug in the program". What if there are multiple concurrent writers to the same channel and one of them decide to close the channel without having to inform all the other goroutines? – Sreram May 19 '21 at 13:32
  • 2
    @Sreram you can have multiple channels instead of having multiple writers to the same channel. and then close each channel. on the receiving side you can use a select to pick from each channel when they are ready. – masonCherry Jul 16 '21 at 17:53
0

Go channels are designed for 1 writer, and multiple readers. Thus the writer has to create, share, and close the channel once is done writing. In a way, the writer owns the channel. Although you could have multiple writers on a single channel (this is not advised/recommended).

Macc Etzel
  • 31
  • 1