在 Go 中枚举常量的最佳方法

Best way to enumerate constants in Go

我正在学习其他语言之后开始学习 Go。 Go 有一个非常 elegant way of creating constants 的数值,例如:

const (
    _      = iota    // 0 and is skipped
    Sunday           // 1
    Monday           // 2
    ...
)

这个写起来很容易,但是真的容易维护吗?例如,如果你突然在 between present 中插入新的值,所有后续的值都会改变。而且很难找到,只有仔细阅读差异才能揭示它。或者其他部分的错误。我怎样才能用名称提取这些值并在程序的其他部分或数据库中使用? 例如对于 PostgreSQL 我可以定义:

CREATE TYPE color AS ENUM ('', 'Sunday', 'Monday');

只是为了说明一个想法。例如,Python has Enum 类型:

from enum import Enum
class Color(Enum):
    RED = 1
    GREEN = 2
    BLUE = 3

那你就可以像Color.RED一样使用它了。接下来我可以取所有值:

list(Color)
[<Color.RED: 1>, <Color.BLUE: 2>, <Color.GREEN: 3>]

这使我能够 "introspect" 在数据库中模块化和创建易于阅读的枚举。例如对于 PostgreSQL 我可以定义:

CREATE TYPE color AS ENUM ('RED', 'GREEN', 'BLUE');

我怎样才能:

  1. 反映 golang 常量名称?
  2. 制作不会漂移的防错常量?只能手动修复它们吗?
  3. 是否有更好的惯用方法?

谢谢。

1) 您可以使用 stringer 生成名称 https://godoc.org/golang.org/x/tools/cmd/stringer

2) 不确定你的意思?大多数语言都允许你漂移值,如果你想让数字保持不变,你应该总是添加到列表的末尾,或者像 python 一样,你可以明确地将每个值设置为一个数字,而不是使用 iota。

3) 不完全是,枚举在 golang 中不是很好

只是一个建议,但可能对您的情况有所帮助:我发现如果很明显这些值看起来像位掩码,那么以后常量不太可能是 changed/broken,您可以在像这样:

const (
    Red   = 1 << iota
    Green
    Blue
) // values = 1, 2, 4

而且,即使它不是最漂亮的声明,您也可以包含掩码常量

const (
    Red, RedMask = 1 << iota, 1<< iota - 1 // Red = 1, RedMask = 0
    Green, GreenMask                       // Green = 2, mask = 1
    Blue, BlueMask                         // 4, 3
    RGB, RGBMask                           // 8, 7
)

这与为这些常量指定的 type 相结合可能会有用:

type ColourConst int
const (
    Red, RMask ColourConst = 1 << iota, 1 << iota-1
    // ...
    _, All
)

// something like this (untested, might not be correct)
func (c ColourConst) validate() error {
    mask := int(c) & (-1 * int(c))
    if mask != int(c) {
        return errors.New("Colour is not a single bit value")
    }
    if s := c & All; s != c {
        return errors.New("Colour is not in range")
    }
}

我知道星期几不太可能用作位掩码,但它可以降低人们破解密码的可能性。至少,它表明常量的顺序很重要,这就是 iota IMO 的作用。

解决方案。 有优秀的模块 Enumer 和 Enumelinter