Для разных конкретных типов каналов можно применять reflect.Select и возвращать chan any. Обобщённая merge[T] проще, но требует одинакового T у всех входов одного вызова.
Слияние данных из нескольких каналов в единый поток
Для разных конкретных типов каналов можно применять reflect.Select и возвращать chan any. Обобщённая merge[T] проще, но требует одинакового T у всех входов одного вызова.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
Реализуйте функцию, которая принимает несколько каналов и собирает все поступающие из них значения, формируя один общий результат. Функция должна корректно работать с каналами разного типа и обеспечивать завершение после обработки всех входных потоков.
Ответ
Для разных конкретных типов каналов можно применять reflect.Select и возвращать chan any. Обобщённая merge[T] проще, но требует одинакового T у всех входов одного вызова.
Реализация с импортом reflect:
func merge(channels ...any) <-chan any {
cases := make([]reflect.SelectCase, 0, len(channels))
for _, ch := range channels {
value := reflect.ValueOf(ch)
if !value.IsValid() { continue }
if value.Kind() != reflect.Chan || value.Type().ChanDir() == reflect.SendDir {
panic("expected receive-capable channel")
}
if value.IsNil() { continue }
cases = append(cases, reflect.SelectCase{Dir: reflect.SelectRecv, Chan: value})
}
out := make(chan any)
go func() {
defer close(out)
for len(cases) > 0 {
chosen, value, ok := reflect.Select(cases)
if !ok {
cases[chosen] = cases[len(cases)-1]
cases = cases[:len(cases)-1]
continue
}
out <- value.Interface()
}
}()
return out
}
Nil-каналы пропускаются, закрытые удаляются из набора. Порядок произволен. Для раннего ухода получателя нужен context; без него результат читают до конца. Reflection дороже типизированного fan-in, число case имеет ограничения runtime.