我想创建一个如下所示的常量映射:
const (
running = map[string]string{
"one": "ONE",
"two": "TWO",
}
)
Run Code Online (Sandbox Code Playgroud)
但每当我这样做时,我会收到以下错误:
const initializer map[string]string literal is not a constant
为什么会这样,为什么Golang不像其他变量一样对待它们?
One*_*One 19
来自https://golang.org/ref/spec#Constants:
常量值由符文,整数,浮点,虚数或字符串文字表示,表示常量的标识符,常量表达式,结果为常量的转换或某些内置的结果值函数如unsafe.Sizeof应用于任何值,cap或len应用于某些表达式,real和imag应用于复数常量和复数应用于数值常量.
tl; dr只有数字类型,字符串和bool可以是常量,数组,切片和映射不是数字类型.
我的看法是,这个决定纯粹是务实的:Go 是一种非常务实的语言(与其他——“更纯粹”——的语言相反),一些现实世界地图实现的一个有趣的特性就是访问它们进行阅读可能会更新它们的内部表示(!)。比如说,他们可能会收集并存储一些关于其使用情况的统计信息,或者他们可能会重新平衡包含值桶等的底层树。允许“常量映射”存在意味着在语言规范中明确指定一组复杂的约束— 最有可能需要实现有两个地图实现。
您也可以尝试从另一个角度来看它:考虑一个字符串常量。这样的东西可能很容易嵌入到.rodata生成的二进制文件的部分中,并且实际上由该数据在内存中的地址表示(好吧,Go 中的字符串更复杂,但让我们忽略该细节)。也就是说,常量字符串可以是真正的“静态”:它只是内存中的一系列静态 R/O 字节——就这么简单。相反,地图是由复杂机器驱动的高度复杂的野兽,每个地图都是在运行时实例化的特殊复杂对象。 这就是为什么你甚至不能仅仅声明一个映射并使用它:你必须make()首先使用它——就像通道一样,出于同样的原因。
同样,可以进行一些 hack 来支持常量映射。比如说,一个实现可以预先对映射的键进行排序,将它(带有值)序列化到 R/O 数据的连续区域中,然后在运行时使用二进制搜索来查找值。对于大地图/某些关键模式,这将是非常无效的,但据说可以工作。尽管如此,这将是一种与“正常”完全不同的专门地图实现。我认为 Go 开发人员认为这种权衡不值得可能的好处。
两个后续注意事项:
如您所见,您可以相对轻松地模拟只读映射:将某些结构类型的切片文字嵌入键和值,对键进行预排序,然后将其包装在对键执行二分搜索的函数中。
我更喜欢认为 Go 的常量有点像 C 类语言中的宏:它们是无类型的,感觉像是文本(它们不是,但我毕竟是在谈论感觉);-)
请务必阅读本文以获得一个很好的概述。