• Go 常量为什么只支持基本数据类型?


    大家好,我是煎鱼。

    相信大家在接触 Go 这门编程语言时,就会学到常量这个知识点。

    各大编程语言会教你,常量是不可变变量的一种类型。只要定义了常量,你就可以安心的用他。不用担心值在哪里就被程序莫名奇妙的给改了。

    常量的使用例子

    如下例子:

    1. const s string = "脑子进煎鱼了"
    2. func main() {
    3.  fmt.Println(s)
    4.  const n = 500000000
    5.  const d = 3e20 / n
    6.  fmt.Println(d)
    7.  fmt.Println(int64(d))
    8.  fmt.Println(math.Sin(n))
    9. }

    输出结果:

    1. 脑子进煎鱼了
    2. 6e+11
    3. 600000000000
    4. -0.28470407323754404

    你可能会发现一个奇怪的点。那就是例子里都是基本的数据类型。那能不能用复杂点的数据类型呢,例如 Go 里比较经典的切片。

    如果是用 var 声明:

    1. var s = []string{"摸""煎""鱼"}
    2. func main() {
    3.  fmt.Println(s)
    4. }

    正常输出:[摸 煎 鱼]

    如果是用 const 声明:

    const s = []string{"摸""煎""鱼"}

    将会报错:

    ./prog.go:7:11: []string{…} (value of type []string) is not constant

    因为在 Go 中,仅支持字符、字符串、布尔和数值类型的常量。这和其他编程语言间多多少少还是有点不一样的。

    为什么不支持更多类型

    为什么在 Go 中常量只支持这几种基础类型,为什么没法支持复杂类型,或是所有类型?

    这是非常 “奇怪” 的。毕竟 PHP 都能支持:

    1. class MyClass
    2. {
    3.     const ABC = array('A''B''C');
    4.     const A = '1';
    5.     const B = '2';
    6.     const C = '3';
    7.     const NUMBERS = array(
    8.         self::A,
    9.         self::B,
    10.         self::C,
    11.     );
    12. }

    这又是出于什么缘由?

    已有 10 年老提案

    我认真翻阅了 Go issues 等相关资料,终于发现竟然在 2013 年(10 年前),就已经有人提出过这个提案:

    28e111623aecd705363d752503a36ee4.png

    提出者 @RickySeltzer,认为:

    var each1 = []byte{'e''a''c''h'}

    可以正常运行。

    const each2 = []byte{'e''a''c''h'}

    不行,直接报错:prog.go:7: const initializer []byte literal is not a constant

    他认为这就是 Go 语言规范和设计上的缺陷,得改,得支持!

    拒绝的论据是什么

    对此也有各种争论,老大哥 @Robert Griesemer,10 年前已 在为 Go 贡献,现在依然还在...他对 Go 常量只支持基本类型这个设计给出了定论。

    基于如下原因:

    • 首先这不是语言的缺陷,也不是设计的缺陷。Go 语言的常量是故意设计成只支持基本类型。

    • 基本类型到复杂类型的改变,这种变化的影响比我们看到的要深远得多。需要考虑很多问题。

      • 常量 channel、常量指针、常量 maps、常量 slice 都要支持吗?

      • 先支持常量 array 和 常量 struct?

      • 要支持到什么程度,以什么来作为标准决定?

    • 设计上,Go 团队期望类型系统(包括常量的含义)相对简单,以免在编译时出现问题。

      • 常量更多类型的支持,会增加复杂性。不清楚这样做的好处是否值得(ROI)。

      • 常量是否可以寻址?const 数组的元素本身必须是 const 吗?

    汇总一下,潜台词就是:

    1. 设计如下:常量只支持基本类型,是 Go 设计上就是这么决定的。

    2. ROI 要衡量一下:没有支持更多的复杂类型,是认为好处不多。也没有想清楚这块的缘由。

    3. 少即是多:类型系统要保持少即是多(less is more)。

    实战中的骚操作

    在相关提案的 issues 中看到欧神(@Changkun Ou)也参与了讨论,他例举了一个常量的经典骚操作。

    代码如下:

    1. const ErrVerification = errors.New("crypto/rsa: verification error")
    2. var ErrVerification = errors.New("crypto/rsa: verification error")

    在对应的 Go 工程中可能是这么使用:

    1. import "cropto/rsa"
    2. func init() {
    3.  rsa.ErrVerification = nil
    4. }

    这个场景我还真见过,一开始想定义 const,结果不支持。只能被迫 var。这不,就开天窗了。

    总结

    Go 核心团队在综合衡量 ROI 后,认为常量支持更多的类型在可预见的未来不会改变。因为将常量的概念扩展到基本类型之外将是一个重大变化,会产生各种影响。

    在此刻(Go1)改变语言的门槛非常高,需要有明确的定义,要有完整的提案以及对成本和效益等的详细分析。

    如果出现,只可能在未来的 Go2。但结合 rsc 最新的结论,没有 Go2 的话,那如果未来真的做,那就是通过 GODEBUG 和 go.mod 的 go 版本号来实现了。

    常量更多类型的支持,虽然已经提出了 10 年。但目前 Go 核心团队推进乏力,想必,可能还要个 5 年吧。

    推荐阅读

    关注和加煎鱼微信,

    一手消息和知识,拉你进技术交流群👇

    100b32168d09b15b9c01be546fcd2e60.jpeg

    b750269e890ba4cc39301b076bf9a184.png

    你好,我是煎鱼,出版过 Go 畅销书《Go 语言编程之旅》,再到获得 GOP(Go 领域最有观点专家)荣誉,点击蓝字查看我的出书之路

    日常分享高质量文章,输出 Go 面试、工作经验、架构设计,加微信拉读者交流群,和大家交流!

  • 相关阅读:
    小程序容器解决OA系统数字化升级难题?
    Swift 判断 A B 两个时间是不是同一天,A 是不是 B 的昨天
    Codeforces Round 905 Div 1 (CF1887)
    Android10 dex2oat实践
    从小公司功能测试到一线大厂自动化测试,薪资翻倍,我做到了...
    多线程Thread(初阶一:认识线程)
    Java并发常见面试题
    海康视觉算法平台VisionMaster 4.3.0 C# 二次开发01 加载方案并获取结果
    数据首发,智驾域控制器「重构芯片格局」,谁在领跑汽车新赛道
    解绑数字身份,解锁新玩法与构建方式(下)
  • 原文地址:https://blog.csdn.net/EDDYCJY/article/details/132867570