Ответ
В Go нет встроенной поддержки тип-сумм (также известных как алгебраические типы данных), но этот паттерн можно эффективно эмулировать с помощью интерфейсов и неэкспортируемого метода-маркера.
Тип-сумма — это тип данных, который может принимать значение одного из нескольких определенных, заранее известных типов.
Пример эмуляции в Go
Представим, что у нас есть фигура, которая может быть либо кругом, либо прямоугольником. Мы можем смоделировать это так:
package shapes
import "math"
// Shape — это наш тип-сумма. Только типы из этого пакета могут его реализовать.
type Shape interface {
// isShape — неэкспортируемый метод-маркер.
// Он не дает типам из других пакетов реализовать этот интерфейс.
isShape()
}
type Circle struct {
Radius float64
}
func (c Circle) isShape() {}
type Rectangle struct {
Width, Height float64
}
func (r Rectangle) isShape() {}
// Area безопасно работает с любым типом, реализующим Shape
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("неизвестный тип фигуры")
}
}
Как это работает:
- Закрытый интерфейс: Мы создаем интерфейс
Shapeс неэкспортируемым методом (isShape). Поскольку метод начинается с маленькой буквы, его могут реализовать только типы, объявленные в том же пакете (shapes). Это эффективно "закрывает" набор возможных типов. - Реализации: Каждый конкретный тип (
Circle,Rectangle) реализует этот интерфейс, предоставляя пустую реализацию метода-маркера. - Type Switch: При обработке значения типа
Shapeиспользуетсяtype switchдля безопасного определения конкретного типа и выполнения соответствующей логики. Это гарантирует, что мы обработаем все возможные варианты, определенные в нашем пакете.
Ответ 18+ 🔞
А, блядь, вот смотри, в Go же нихуя нету встроенного для этих ваших тип-сумм, ну или алгебраических типов данных, как их там умные называют. Но это же не повод расстраиваться, на то он и Go, чтобы выебываться и эмулировать всё через интерфейсы да неэкспортируемые методы-маркеры, ёпта.
Тип-сумма — это, грубо говоря, такая штука, которая может быть чем-то одним из заранее известного набора вариантов. Как будто у тебя в кармане либо ключи, либо зажигалка, а хули — телефон уже в руке.
Давай на примере, как это в Go выкручиваются
Допустим, у нас есть геометрическая фигура. Она может быть либо кругом, либо прямоугольником. И всё, блядь, других вариантов не дано. Моделируем эту пизду:
package shapes
import "math"
// Shape — это наш главный интерфейс, типа-сумма.
// Только свои, родные типы из этого пакета смогут его реализовать, потому что...
type Shape interface {
// isShape — вот этот вот маленький, неэкспортируемый метод-маркер.
// Он как печать на жопе: чужих не пущать.
isShape()
}
type Circle struct {
Radius float64
}
func (c Circle) isShape() {} // Печать поставлена.
type Rectangle struct {
Width, Height float64
}
func (r Rectangle) isShape() {} // И тут печать.
// Area — функция, которая уже может спокойно работать с любой фигурой.
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("неизвестный тип фигуры")
}
}
А теперь, сука, разбор полётов:
- Интерфейс с секретиком: Создаём интерфейс
Shape, но внутри у него методisShape()с маленькой буквы. Это как закрытый клуб: войти могут только те, кто прописан в этом же пакете (shapes). Таким образом мы и закрываем набор возможных вариантов, чтоб никакой левыйTriangleс улицы не подсунули. - Легальные члены клуба: Каждый конкретный тип (
Circle,Rectangle) тихо-мирно реализует этот интерфейс, делая пустую заглушку для метода-маркера. Всё, пропуск получен. - Волшебный свитч: Когда нам в функцию
Areaприлетает что-то типаShape, мы делаемtype switch. Это как тактильный осмотр в темноте: нащупал круг — считай площадь круга, нащупал прямоугольник — считай прямоугольник. И мы уверены, что других, неучтённых хуйней тут быть не может (если, конечно, не накосячили в самом пакете). Дефолтная ветка — это просто паранойя на максималках.
Вот и вся магия, блядь. Никаких вам волшебных enum'ов, всё на честном слове и интерфейсах. Зато работает, ёпта.