新提案:创建指向简单类型的指针的表达式
大家好,我是煎鱼。
在 Go 语言中,我们经常要声明各种变量,再进行赋值,才能开始写程序。
常见的会是使用 new 或 make,甚至会有技术文章《Go 面试题: new 和 make 是什么,差异在哪?》探讨什么 new 和 make 的区别。
也有特例,是直接使用 &S{}
来作为构建指向结构体的指针的方法。这奇怪点是,只有结构体可以用,分配方式是半隐藏的。
背景
这可能叫做创作这门语言的 Go 语言之父 Rob Pike 对自己创作的语法产生了奇妙的疑惑。
在新提案《proposal: expression to create pointer to simple types》中提出了这个问题。
问题
他发现结构体这种分配方式,更容易。而创建一个简单类型的指针,会更麻烦一些。如下:
// 结构体
p := &S{a:3}
// 简单类型
a := 3
p := &a
也就是在一门语言中不同的数据类型的处理方式都不一样,而且简单类型比复合的结构体更难初始化变量。
新建议
第一种
第一种方式是给函数 new
增加可选参数。函数签名如下:
new(typeOfExpression, Expression)
演示案例如下:
p1 := new(int, 3)
p2 := new(rune, 10)
p3 := new(Weekday, Tuesday)
p4 := new(Name, "unspecified")
p5 := new(T, f())
... and so on
这样可以将 Go 的多种数据类型方式完全纠正过来,实现统一。但是内置函数 new
会变的冗长,会有的人不喜欢。
第二种
第二种方式是内置新的可寻址方式来解决。可能有小伙伴以为是:
p := &3
并不是,这样是没法知道 3 是什么类型,自然编译器也无法识别,这个脑洞没法实现。
演示案例如下:
p1 := &int(3)
p2 := &rune(10)
p3 := &Weekday(Tuesday)
p4 := &Name("unspecified")
这样相当于给所有数据类型增加了一个 &
转换的过程,让他们能够进行寻址。
讨论
社区内大家对此议论纷纷,给出了各种方案。 我梳理了一下,基本分为以下三类建议:
- 可以给基本类型加初始化,例如:
&int{3}
、&bool(true)
、&string("brains")
,效果和&S{}
一样。 - 可以改造函数
new
,例如:new(int(3))
,也能解决基本类型的两行声明。 - 可以利用泛型,加一个函数实现类似的效果,例如:
func NewOf[T any](v T) *T
。
甚至还有人唱反调,认为社区中有其他人提出,却不被受重视。但是 Rob Pike 一提出,就能够一呼百应。
(相当于是嘲讽了…)
总结
我们针对平时 Go 编程中常常用到的取地址场景进行了讲到,不说的话,其实很多同学没发现这个细节:也就是结构体可以取,但是基本类型不能。
Go 核心团队也在不断地想要统一和优化这个用法。加上原有的提案上的两种方式,其实就有 5 类建议了,你比较认可哪种呢?