Go 常量只支持基本数据类型?为什么?社区撕了 9 年了...

453 阅读3分钟

大家好,我是煎鱼。

今天给大家分享的一个提案,已经在 Go 社区讨论了整整 9 年(2013~2022),它与我们的日常编程密切相关。

今天就由煎鱼和大家一起深入学习和了解提案《proposal: spec: allow constants of arbitrary data structure type》吧,看看有没有什么新的想法。

背景

我们先看看以下示例代码,如下:

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》来看,指向的都是同类诉求。

目的是能声明类似:

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 官方对此常量的类型支持非常谨慎,认为收益不明确、复杂度有可能增高,认为简单就好了。

你又怎么看呢?欢迎在评论区留言和讨论。

文章持续更新,可以微信搜【脑子进煎鱼了】阅读,本文 GitHub github.com/eddycjy/blo… 已收录,学习 Go 语言可以看 Go 学习地图和路线,欢迎 Star 催更。

Go 图书系列

更多阅读