← Назад к вопросам

Какого типа данных не хватает в Go на твой взгляд

2.7 Senior🔥 71 комментариев
#Основы Go

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Какие типы данных могли бы дополнить Go

На мой взгляд, отсутствие дженериков (обобщённого программирования) до версии 1.18 было самым значительным пробелом в системе типов Go. Хотя эта проблема теперь решена, остаются несколько типов данных и конструкций, которые могли бы улучшить язык.

1. Алгебраические типы данных (ADT) и pattern matching

Наиболее востребованный недостающий элемент — это алгебраические типы данных (sum types, discriminated unions) и сопоставление с образцом (pattern matching). В Go часто приходится моделировать варианты через интерфейсы, что приводит к многословности и необходимости проверок типа через type assertion.

// Текущий подход (громоздкий)
type Shape interface { isShape() }

type Circle struct { Radius float64 }
func (c Circle) isShape() {}

type Rectangle struct { Width, Height float64 }
func (r Rectangle) isShape() {}

// Приходится делать type switch каждый раз
func Area(s Shape) float64 {
    switch v := s.(type) {
    case Circle:
        return math.Pi * v.Radius * v.Radius
    case Rectangle:
        return v.Width * v.Height
    default:
        panic("неизвестная фигура")
    }
}

Идеальным решением было бы добавление синтаксиса для объединённых типов и exhaustive pattern matching:

// Гипотетический синтаксис
type Shape union {
    Circle { Radius float64 }
    Rectangle { Width, Height float64 }
}

func Area(s Shape) float64 {
    match s {
        case Circle { Radius: r }:
            return math.Pi * r * r
        case Rectangle { Width: w, Height: h }:
            return w * h
    } // компилятор проверяет полноту покрытия
}

2. Номинальные типы-псевдонимы (newtype)

В Go есть type, но он создаёт отдельный тип только для структур. Для примитивов часто хочется иметь номинальные типы-обёртки, которые обеспечивали бы безопасность типов на этапе компиляции, но при этом не требовали бы явного приведения в runtime.

// Текущая проблема: type alias не даёт безопасности типов
type UserID = int
type OrderID = int

func GetUser(id UserID) { /* ... */ }

var oid OrderID = 100
GetUser(oid) // Компилируется, хотя это логическая ошибка!

// Желаемое поведение:
type UserID distinct int
type OrderID distinct int

func GetUser(id UserID) { /* ... */ }

var uid UserID = 100
var oid OrderID = 200
GetUser(uid) // OK
GetUser(oid) // Ошибка компиляции: несовместимые типы

3. Immutable-типы и readonly-ссылки

Отсутствие неизменяемых (immutable) структур данных и readonly-ссылок затрудняет написание безопасного конкурентного кода. Приходится полагаться на дисциплину программиста и копирование данных.

// Сегодня мы вынуждены делать так:
type Config struct {
    Timeout time.Duration
    Retries int
}

func (c Config) WithTimeout(t time.Duration) Config {
    c.Timeout = t // Работает с копией
    return c
}

// Хотелось бы иметь:
type Config struct {
    Timeout time.Duration
    Retries int
} immutable // Все поля становятся readonly после создания

// Или хотя бы:
func ValidateConfig(c readonly *Config) error {
    // Не можем изменить c.Timeout здесь
    return nil
}

4. Option/Maybe и Result/Either типы

Хотя дженерики позволяют реализовать эти типы в пользовательском коде, их отсутствие в стандартной библиотеке приводит к фрагментации. Option для nullable-значений и Result для обработки ошибок сделали бы код более выразительным и безопасным.

// Вместо:
func Divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, errors.New("деление на ноль")
    }
    return a / b, nil
}

// Можно было бы иметь:
func Divide(a, b float64) result.Float64 {
    if b == 0 {
        return result.Err[float64](errors.New("деление на ноль"))
    }
    return result.Ok(a / b)
}

// И работать с цепочками:
Divide(10, 2).
    Map(func(x float64) float64 { return x * 2 }).
    AndThen(func(x float64) result.Float64 { 
        return Divide(x, 5) 
    })

5. Диапазоны (range types) для примитивов

Полезным было бы иметь встроенную поддержку типов-диапазонов для чисел и дат с проверкой на этапе компиляции или рантайма:

type Percentage range(0, 100)

func SetBrightness(p Percentage) {
    // Нет необходимости проверять границы
}

p := Percentage(50) // OK
p2 := Percentage(150) // Ошибка компиляции или паника

Почему эти типы до сих пор не добавлены

Философия Go сознательно жертвует выразительностью в пользу простоты, читаемости и быстрой компиляции. Каждое нововведение:

  • Усложняет компилятор
  • Увеличивает время обучения
  • Может нарушить обратную совместимость
  • Добавляет cognitive load при чтении чужого кода

Тем не менее, с добавлением дженериков появился прецедент для расширения системы типов. Алгебраические типы данных — наиболее вероятный кандидат на следующее крупное дополнение, так как они решают реальные проблемы безопасности типов и полноты обработки вариантов, оставаясь относительно простыми для понимания.

Какого типа данных не хватает в Go на твой взгляд | PrepBro