8

Как идиоматично представлять перечисления (enum) в Go?

1

Я пытаюсь представить упрощенную хромосому, которая состоит из N оснований, каждое из которых может быть только одним из {A, C, T, G}.

Я хотел бы формализовать ограничения с помощью перечисления (enum), но меня интересует, какой самый идиоматичный способ эмуляции перечислений в языке Go.

5 ответ(ов)

1

Ссылаясь на ответ jnml, вы можете предотвратить создание новых экземпляров типа Base, если не экспортировать его вовсе (то есть написать его с маленькой буквы). При необходимости вы можете создать экспортируемый интерфейс, который будет иметь метод, возвращающий тип base. Этот интерфейс можно использовать в функциях снаружи, которые работают с Base, например:

package a

type base int

const (
    A base = iota
    C
    T
    G
)

type Baser interface {
    Base() base
}

// каждый base должен реализовывать интерфейс Baser
func (b base) Base() base {
    return b
}

func (b base) OtherMethod() {
}

package main

import "a"

// функция снаружи, которая работает с a.base через a.Baser
// поскольку a.base не экспортирован, можно использовать только экспортируемые базы, созданные внутри пакета a, такие как a.A, a.C, a.T и a.G
func HandleBasers(b a.Baser) {
    base := b.Base()
    base.OtherMethod()
}

// функция снаружи, которая возвращает a.A или a.C в зависимости от условия
func AorC(condition bool) a.Baser {
    if condition {
        return a.A
    }
    return a.C
}

Внутри пакета main интерфейс a.Baser фактически работает как перечисление (enum). Только внутри пакета a вы можете определять новые экземпляры.

0

Вы можете сделать это следующим образом:

type MessageType int32

const (
    TEXT   MessageType = 0
    BINARY MessageType = 1
)

С помощью этого кода компилятор будет проверять тип перечисления (enum). Чтобы обеспечить типобезопасность, можно добавить метод для вашего типа MessageType, который будет проверять корректность присваиваемого значения. Однако, данный код уже предоставляет некоторую степень проверки благодаря определению типа MessageType, поэтому вы не сможете случайно присвоить значение другого типа.

0

Действительно, приведённые выше примеры использования const и iota — это наиболее идиоматичные способы представления примитивных перечислений в Go. Но если вы хотите создать более функциональное перечисление, похожее на тип, который вы могли бы увидеть в других языках, таких как Java или Python, вы можете воспользоваться следующим подходом.

Очень простой способ создать объект, который начинает походить на строковое перечисление в Python, выглядит так:

package main

import (
    "fmt"
)

var Colors = newColorRegistry()

func newColorRegistry() *colorRegistry {
    return &colorRegistry{
        Red:   "red",
        Green: "green",
        Blue:  "blue",
    }
}

type colorRegistry struct {
    Red   string
    Green string
    Blue  string
}

func main() {
    fmt.Println(Colors.Red)
}

Предположим, что вам также нужны некоторые вспомогательные методы, такие как Colors.List() и Colors.Parse("red"). Если ваши цвета более сложные и должны быть структурой, вы можете сделать что-то вроде этого:

package main

import (
    "errors"
    "fmt"
)

var Colors = newColorRegistry()

type Color struct {
    StringRepresentation string
    Hex                  string
}

func (c *Color) String() string {
    return c.StringRepresentation
}

func newColorRegistry() *colorRegistry {
    red := &Color{"red", "F00"}
    green := &Color{"green", "0F0"}
    blue := &Color{"blue", "00F"}
    
    return &colorRegistry{
        Red:    red,
        Green:  green,
        Blue:   blue,
        colors: []*Color{red, green, blue},
    }
}

type colorRegistry struct {
    Red   *Color
    Green *Color
    Blue  *Color

    colors []*Color
}

func (c *colorRegistry) List() []*Color {
    return c.colors
}

func (c *colorRegistry) Parse(s string) (*Color, error) {
    for _, color := range c.List() {
        if color.String() == s {
            return color, nil
        }
    }
    return nil, errors.New("не удалось найти")
}

func main() {
    fmt.Printf("%s\n", Colors.List())
}

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

0

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

Однако стоит отметить, что в этом случае мы можем использовать только var, а не const. Это приводит к некоторым ограничениям, так как var переменные обладают меньшей производительностью по сравнению с const.

Вот пример того, как это может быть реализовано:

type OrderStatusType string

var OrderStatus = struct {
    APPROVED         OrderStatusType
    APPROVAL_PENDING OrderStatusType
    REJECTED         OrderStatusType
    REVISION_PENDING OrderStatusType
}{
    APPROVED:         "approved",
    APPROVAL_PENDING: "approval pending",
    REJECTED:         "rejected",
    REVISION_PENDING: "revision pending",
}

В этом примере все значения статуса заказа находятся в OrderStatus, что позволяет использовать их в коде, не беспокоясь о конфликте имен.

0

Для данного случая может быть полезно использовать строковую константу, чтобы ее можно было сериализовать в JSON-строку. В следующем примере []Base{A, C, G, T} будет сериализовано в ["adenine", "cytosine", "guanine", "thymine"].

type Base string

const (
    A Base = "adenine"
    C      = "cytosine"
    G      = "guanine"
    T      = "thymine"
)

При использовании iota значения будут сериализованы в целые числа. В следующем примере []Base{A, C, G, T} будет сериализовано в [0, 1, 2, 3].

type Base int

const (
    A Base = iota
    C
    G
    T
)

Вот пример, сравнивающий оба подхода:

https://play.golang.org/p/VvkcWvv-Tvj

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь