# Closing Channels, Ranging and nil Channels — Go Concurrency Patterns

Source: https://www.skillbyai.com/en/go-concurrency/gc-closing

> Who closes, what receivers see, and the three special cases.

## The rules of close

`close(ch)` means **no more values will be sent**. After close, receivers first drain any buffered values, then every receive returns immediately with the **zero value**; the two-value form `v, ok := <-ch` reports `ok == false` once the channel is closed and empty. `for v := range ch` loops until that happens. Sending on a closed channel **panics**, closing a closed channel **panics**, and closing a nil channel **panics**. Hence the rule: **only the sender closes**, and when there are several senders, a coordinator closes the channel after all of them are done (typically after `wg.Wait()`). Closing is optional; it is a signal, not a resource cleanup, so close only when receivers need to know the stream has ended. A **nil channel** (the zero value of a channel variable) blocks forever on both send and receive, which is a bug in ordinary code but a useful tool inside `select`.

## Many senders, one closer

The coordinator closes after every sender has finished.

```go
package main

import (
	"fmt"
	"sync"
)

func main() {
	results := make(chan int)
	var wg sync.WaitGroup

	for i := 1; i <= 3; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			results <- i * i // Go 1.22+: each iteration has its own i
		}()
	}

	// Close only after all senders are done, in its own goroutine
	// so that main can start receiving straight away.
	go func() {
		wg.Wait()
		close(results)
	}()

	for r := range results { // ends when results is closed and drained
		fmt.Println(r)
	}

	v, ok := <-results
	fmt.Println(v, ok) // 0 false: closed channels yield the zero value
}
```

## Use chan struct{} for pure signals

A `chan struct{}` carries no data, so `close(done)` is a clean broadcast: every goroutine waiting on `<-done` wakes up at once, and later receivers return immediately too.

**Quiz:** What does a receive from a closed, empty channel do?

- [ ] Blocks forever
- [x] Returns immediately with the zero value and ok == false
- [ ] Panics
- [ ] Returns the last value that was sent

*Answer:* Returns immediately with the zero value and ok == false. Only sending on or closing a closed channel panics; receiving is always safe.
