Какие подходы к обобщенному программированию (generics) существуют в Go?

Ответ

Подход Go к обобщенному программированию эволюционировал. Исторически использовались интерфейсы, но с версии 1.18 появились полноценные дженерики. Вот основные подходы:

  1. Дженерики (Generics, с Go 1.18) — наиболее современный и типобезопасный способ. Позволяет писать функции и структуры данных, работающие с любым типом, который удовлетворяет заданным ограничениям (constraints).

    // Эта функция работает со слайсом любого типа T
    // и функцией, преобразующей T в U.
    func Map[T, U any](collection []T, f func(T) U) []U {
        result := make([]U, len(collection))
        for i, item := range collection {
            result[i] = f(item)
        }
        return result
    }
  2. Интерфейсы (interface) — классический механизм полиморфизма в Go. Тип удовлетворяет интерфейсу неявно, если реализует все его методы. Это позволяет писать код, работающий с любым типом, который реализует определенное поведение.

    // Функция Copy работает с любыми типами,
    // удовлетворяющими интерфейсам io.Writer и io.Reader.
    func Copy(dst io.Writer, src io.Reader) (int64, error)
  3. Пустой интерфейс (interface{}) — исторический способ эмуляции дженериков до версии 1.18. interface{} может содержать значение любого типа, но для работы с ним требуется проверка типа (type assertion), что небезопасно и менее производительно.

    func PrintAnything(v interface{}) {
        // Требуется проверка типа, чтобы безопасно использовать значение
        if str, ok := v.(string); ok {
            fmt.Println("Это строка:", str)
        }
    }
  4. Рефлексия (reflect) — мощный, но медленный и сложный механизм для работы с типами в рантайме. Используется в крайних случаях, например, при создании фреймворков или сериализаторов, когда типы неизвестны на этапе компиляции.

Итог: Дженерики являются предпочтительным выбором для обобщенного программирования. Интерфейсы остаются ключевым инструментом для полиморфизма и определения поведений. Пустой интерфейс и рефлексию следует использовать с осторожностью.