浅析Go常量为什么只支持基本数据类型

 更新时间:2023年09月13日 14:17:31   作者:煎鱼  
这篇文章主要来和大家一起讨论一下Golang中常量为什么只支持基本数据类型,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起了解一下

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

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

常量的使用例子

如下例子:

const s string = "脑子进煎鱼了"
func main() {
    fmt.Println(s)
    const n = 500000000
    const d = 3e20 / n
    fmt.Println(d)
    fmt.Println(int64(d))
    fmt.Println(math.Sin(n))
}

输出结果:

脑子进煎鱼了
6e+11
600000000000
-0.28470407323754404

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

如果是用 var 声明:

var s = []string{"摸", "煎", "鱼"}
func main() {
    fmt.Println(s)
}

正常输出:[摸 煎 鱼]

如果是用 const 声明:

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

将会报错:

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

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

为什么不支持更多类型

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

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

class MyClass
{
    const ABC = array('A', 'B', 'C');
    const A = '1';
    const B = '2';
    const C = '3';
    const NUMBERS = array(
        self::A,
        self::B,
        self::C,
    );
}

这又是出于什么缘由?

已有 10 年老提案

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

提出者 @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 吗?

汇总一下,潜台词就是:

  • 设计如下:常量只支持基本类型,是 Go 设计上就是这么决定的。
  • ROI 要衡量一下:没有支持更多的复杂类型,是认为好处不多。也没有想清楚这块的缘由。
  • 少即是多:类型系统要保持少即是多(less is more)。

实战中的骚操作

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

代码如下:

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

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

import "cropto/rsa"
func init() {
 rsa.ErrVerification = nil
}

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

总结

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

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

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

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

以上就是浅析Go常量为什么只支持基本数据类型的详细内容,更多关于Go常量的资料请关注脚本之家其它相关文章!

相关文章

  • Golang 并发下的问题定位及解决方案

    Golang 并发下的问题定位及解决方案

    由于 gin-swagger 是并发执行的, 输出的日志本身是错位,这就导致无法定义是哪一个结构体缺少 tag 导致的,这篇文章主要介绍了Golang 并发下的问题定位及解决方案,需要的朋友可以参考下
    2022-03-03
  • 浅谈Go1.18中的泛型编程

    浅谈Go1.18中的泛型编程

    本文主要介绍了Go1.18中的泛型编程,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-12-12
  • Go语言指针访问结构体的方法

    Go语言指针访问结构体的方法

    这篇文章主要介绍了Go语言指针访问结构体的方法,涉及Go语言指针及结构体的使用技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-02-02
  • Golang编程并发工具库MapReduce使用实践

    Golang编程并发工具库MapReduce使用实践

    这篇文章主要为大家介绍了Golang并发工具库MapReduce的使用实践,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-04-04
  • Go中过滤范型集合性能示例详解

    Go中过滤范型集合性能示例详解

    这篇文章主要为大家介绍了Go中过滤范型集合性能示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-03-03
  • 如何在Go中使用切片容量和长度

    如何在Go中使用切片容量和长度

    这篇文章主要介绍了如何在Go中使用切片容量和长度,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-11-11
  • 如何在golang中使用shopspring/decimal来处理精度问题

    如何在golang中使用shopspring/decimal来处理精度问题

    本文主要介绍了如何在golang中使用shopspring/decimal来处理精度问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-04-04
  • Go语言中的数据格式(json、xml 、msgpack、protobuf)使用总结

    Go语言中的数据格式(json、xml 、msgpack、protobuf)使用总结

    在分布式的系统中,因为涉及到数据的传输,所以一定会进行数据的交换,此时就要定义数据交换的格式,例如二进制、Json、Xml等等。本文总结了Go语言中的数据格式,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-07-07
  • Go实现基于RSA加密算法的接口鉴权

    Go实现基于RSA加密算法的接口鉴权

    这篇文章主要介绍了Go实现基于RSA加密算法的接口鉴权,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-06-06
  • Golang通脉之map详情

    Golang通脉之map详情

    这篇文章主要介绍了Golang通脉之map,Go语言中提供的映射关系容器为map,其内部使用散列表(hash)实现,map 是一种无序的键值对的集合。map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值 map 是一种集合,所以可以像迭代数组和切片那样迭代它
    2021-10-10

最新评论