Что произойдет при чтении данных из закрытого канала в Go?

Ответ

Да, читать из закрытого канала можно. Эта операция никогда не блокируется и не вызывает панику. Поведение зависит от способа чтения:

  1. С использованием второй переменной ok: Это идиоматический способ проверки, закрыт ли канал. Чтение из закрытого канала немедленно вернет нулевое значение для типа данных канала и false для переменной ok.

    ch := make(chan int, 1)
    ch <- 10
    close(ch)
    
    val, ok := <-ch // val = 10, ok = true (сначала читаем оставшиеся данные)
    val, ok = <-ch // val = 0, ok = false (канал пуст и закрыт)
  2. В цикле for range: Цикл for range автоматически прекратит свою работу, когда канал будет закрыт и все значения из него будут прочитаны.

    ch := make(chan int, 2)
    ch <- 1
    ch <- 2
    close(ch)
    
    // Цикл напечатает 1 и 2, а затем завершится
    for v := range ch {
        fmt.Println(v)
    }
  3. Без проверки ok: Если не использовать вторую переменную, вы будете бесконечно получать нулевые значения, что может привести к ошибкам в логике. Поэтому всегда проверяйте статус канала при чтении.

Ответ 18+ 🔞

А, ну вот, смотри, с каналами-то какая история. Читать из закрытого — можно, блядь, это ж не паника. Никогда не паникует, операция такая. А вот что вернёт — это уже зависит от того, как ты читаешь, хитрая жопа.

Вот смотри, три варианта, как это бывает:

Первый, самый правильный, с проверкой ok. Это типа идиома такая, стандартная. Ты как бы спрашиваешь: «Э, канал, ты ещё живой, или уже всё?». Если канал закрыт и в нём пусто, то тебе вернётся ноль для типа (0 для int, "" для string, nil для указателей) и false в эту самую ok. Всё честно.

ch := make(chan int, 1)
ch <- 10
close(ch)

val, ok := <-ch // val = 10, ok = true (ну, данные же ещё были, ёпта!)
val, ok = <-ch  // val = 0, ok = false (всё, писец, канал пустой и закрыт. Дальше только нули)

Второй, через for range. Вообще красота, блядь. Самый удобный способ. Цикл этот умный, он сам поймёт, когда канал закрыли и всё вычитали, и тихо, мирно завершится. Не надо там никаких ok ловить.

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

// Цикл напечатает 1 и 2, а потом просто выйдет, потому что понял — всё, кончилось.
for v := range ch {
    fmt.Println(v)
}

А третий вариант — это просто тупо читать без проверки. Вот это, блядь, мартышлюшка. Ты будешь как дурак бесконечно получать нулевые значения после закрытия, и программа твоя может сойти с ума, потому что логика поедет. Поэтому, ёпта, всегда проверяй, жив канал или уже приказал долго жить. Не будь как этот Герасим, который нихуя не говорил, пока Муму не утопил.