为什么Go 常量只支持基本数据类型
时间:2022-7-20 作者:smarteng 分类: Go语言
今天给大家分享的一个提案,已经在 Go 社区讨论了整整 9 年(2013~2022),它与我们的日常编程密切相关。
今天就由煎鱼和大家一起深入学习和了解提案《proposal: spec: allow constants of arbitrary data structure type^[1]^》吧,看看有没有什么新的想法。
背景
我们先看看以下示例代码,如下:
package main
import "fmt"
func main() {
var each1 = []byte{'e', 'd', 'd', 'y'}
const each2 = []byte{'e', 'd', 'd', 'y'}
fmt.Printf("each1 is %q\n", each1)
fmt.Printf("each2 is %q\n", each2)
}
运行结果是什么,是正常输出吗,还是?
最终运行结果如下:
./prog.go:7:16: []byte{…} (value of type []byte) is not constant
结果是 Go 编译失败,编译无法通过。
原因是第 7 行(也就是 const 关键字那行)的常量定义有错误,类型为 []byte
的值是不能作为常量来声明定义的。
悟了也雾了,Go 的常量定义有类型限制,为什么?
期望
我们结合另外个提案《proposal: Go 2: add const literals for reference types like structs, maps, and arrays^[2]^》来看,指向的都是同类诉求。
目的是能声明类似:
const keys = [...]string{"煎鱼", "eddycjy", ... }
期待 Go 能够允许定义其他类型的常量,例如:结构体(struct)、字典(map)和数组(array)等,不会再产生编译错误,顺利运行。
接受提案后,提案作者认为可以实现对原有的 Go 代码没有影响,不会存在破坏性升级,可以有效的简化代码等。
你觉得呢,还有没有别的更多的期望?
不支持的原因
Go 核心团队的 @Robert Griesemer 表示:常量是故意设计为只支持基本类型的(从 Go 第一天开始就被构思出来),不是语言的缺陷,也不是设计的缺陷,并认为将其开放的影响比想象中深远。
不知道要将常量的值类型支持要开放到什么程度,支撑到 channel、slice、指针类型?所以在不确定性下,不知道复杂度有多少,官方试图保持类型系统(包括常量是什么)的相对简单。
与此同时,做这件事现阶段还看不到明显的收益。如果要改,仅有可能在 Go2 发生一些新的调整,而不是 Go1。
真实场景
在评论区中看到欧神(@Changkun Ou)留言,举出了 ”常量错误“ 的经典使用场景。
代码如下:
- const ErrVerification = errors.New("crypto/rsa: verification error")
+ var ErrVerification = errors.New("crypto/rsa: verification error")
如果以下代码出现在项目依赖项之一中,则它会破坏整个系统:
import "cropto/rsa"
func init() {
rsa.ErrVerification = nil
}
这是在项目代码中很常见的,最早内部包想定义 const 的常量错误,最后只能被迫 var 流操作一把。
在这些场景下,你的值在哪里被人改了,都不确定,还得查半天,根本原因是 Go 的不可变变量的功能部分缺失。
总结
在今天这篇文章中,我们介绍了 Go 中最常见的常量(const),实际上它仅支持基本类型的定义。而针对它的不足我们结合了提案中的讨论和使用场景进行了说明。
相较而言,Go 官方对此常量的类型支持非常谨慎,认为收益不明确、复杂度有可能增高,认为简单就好了。
你又怎么看呢?欢迎在评论区留言和讨论。
更多阅读
参考资料
[1]proposal: spec: allow constants of arbitrary data structure type: https://github.com/golang/go/issues/6386
[2]proposal: Go 2: add const literals for reference types like structs, maps, and arrays: https://github.com/golang/go/issues/21130