Go内置零值标识符zero

 更新时间:2023年08月23日 14:23:39   作者:脑子进煎鱼了  
大家学习和使用 Go 语言时,有一个神奇的概念:零值(zero-values),所以本文想给大家分享一个关于零值的新提案,目测已经八九不离十了

大家学习和使用 Go 语言时,有一个神奇的概念:零值(zero-values)。零值这个名字是具体谁起的,又是从哪里先开始喊起的,已经难以考究了。

每次有新同学刚开始转语言,工程上总会提到这个,想要试图改变零值。又或是没考虑到,一旦程序用了 0 作为 default 值,又要兼容零值的问题。

今天给大家分享一个关于零值的新提案,目测已经八九不离十了(在发布此文时,该提案已被正式 accepted)。

快速复习零值

基础类型的例子如下:

func main() {
 var i int
 var f float64
 var b bool
 var s string
 fmt.Printf("%v %v %v %q\n", i, f, b, s)
}

输出结果:

0 0 false ""

复合类型的例子如下:

type Person struct {
 Name   string
 Age    int
 Weight float64
}
func main() {
 var p Person
 var m map[int]string
 var s []string
 fmt.Printf("%#v\n%#v\n%#v\n", p, m, s)
}

输出结果:

main.Person{Name:"", Age:0, Weight:0}
map[int]string(nil)
[]string(nil)

类型和零值的映射表格如下:

数据类型零值
int, int8, int16, int32, int640
uint, uint8, uint16, uint32, uint640
uintptr0
float32, float640
byte0
rune0
string"" (empty string)
complex64, complex128(0,0i)
arrays of non-nillable typesarray of zero-values
arrays of nillable typesarray of nil-values

新提案:预声明标识符 zero

背景

事情的起因是:@Nathan Cormier 在社区提了吐槽和提案《proposal: builtin: zero function[1]》:

他表示:Go 语言目前提供了几种获取类型零值的方法,如果能有一种统一的方法来实现这一点,代码的可读性会更高。

有同学深思,哪来的几种?其实可以:

var z MyType
return z

还可以:

return *new(MyType)

初始化结构体后出来的都是零值。

紧接着就会遇到开头提到的问题,你怎么知道这是零值,还是缺省值?难以直接判别。

更重要的是,这不太符合 Go 的设计哲学,又是多种途径,还和缺省值产生混淆。显然和主打工程的 Go 的发展路子不合。

解决方案

新增 zero 标识符

这次经过一番撕扯,最终是由 Go 核心团队负责人 @Russ Cox 出来主持大局,提出了新提案并一路向前。

Go 将会增加一个新的预定义标识符 zero,它是一个无类型的零值,适用于数组和结构体类型。

以下是 zero 的类型签名:

// zero is a predeclared identifier representing the zero value
// for array and struct types.
var zero Type

他不会像 nil 一样。我们可以对照看看 nil 的类型签名:

// nil is a predeclared identifier representing the zero value for a
// pointer, channel, func, interface, map, or slice type.
var nil Type // Type must be a pointer, channel, func, interface, map, or slice type

Nil 受限于 chan、func、interface、map、slice、pointer 等类型。

zero 可赋值性规则

zero 的可赋值性规则如下:

  • 绝大部分场景下,zero 可以分配给任何类型 T 的任何变量。(T 指的是结构体)
  • 不可以的场景,已经具有短零(指的是:0、“”、nil)的变量,不可以分配。
  • 泛型场景下,即使 T 是带有 any 约束条件的类型参数,也是可以分配的。
  • 上述提到的可赋值性,包含函数参数和返回值,例如:f(zero) 和 return zero, err 也是正常有效的,也能够用于值比较。

可能会有的同学担心,zero 和短零(0, "", or nil)产生冲突。对此可以放心。在设计的规则上:zero 在短零有效的任何地方都无效,所以在使用上不会产生混淆。

解决了什么问题

实际问题上,解决了以下几点:

  • 在通用代码中需要引用零值,发现大家经常建议使用 *new(T) 来做初始化。导致经常要向新用户解释这一点非常麻烦。Go 需要更简洁的方法。
  • 在泛型代码中便于与零值比较,在 cmp: add Or[2] 中出现了这一实际编码诉求。
  • 缩短错误返回,新的 return zero, err 会比原有的 return time.Time{}, err 更好。

通用概念来讲:

  • 零值是 Go 中的一个重要概念。但某些类型对此还没有具体的名称,现在可以叫他 zero
  • Go 需要一个机制来将其值和零值进行比较,来解决前文提到的缺省值和零值无法很好识别的问题。

一些问题和争论

会有同学认为,现有的 return _, _, err 会比 return zero, zero, err 更短并且感觉更好。(但在清晰程度上会不如 zero 和 nil)

也有同学会认为自己写一个这个实现也很简单,是否有必要为此专门增加一个 zero 预定义值?

可以用泛型如下实现:

func Zero[T any]() T {
    var v T
    return v
}

对此在很多提案上,都很容易陷入怪圈。有了泛型后,照实现来讲,什么都可以自己写一份。

也会有一派认为以后判断错误类型要变成:

if err != zero {
  ...
}

会非常奇怪。以后可能会出现 if err != nil 和 if err != zero 的奇怪代码场景。

注:这个场景是假定 zero 标识符能适用于所有的数据类型下才会出现。本次并不存在。

总结

目前的社区讨论比较发散,认为 zero 标识符未来也可以取代所有的零值。但从提案内容和提交的 SPEC CL 来看,@Russ Cox 一直针对的是数组和结构体的零值场景去使用 zero 标识符。

这个提案已经进入到最终阶段,已经被正式接受,基本跑不了了。

以后我们在不同类型下对零值判断,可以基于:

数据类型使用什么作为零值
int 等数字类型使用 0
string 字符串类型使用 ”“(空字符串)
slice, map, function, pointer, channel, and interface 等类型使用 nil
array 和 struct 类型可使用 zero 标识符

虽然在使用上,做了一定的使用场景的切分,也就是只有数组和结构体类型使用 zero 标识符。但对于写 Go 用户而言,认知上是会存在明确混淆的。

以后你给其他同学解释零值,解释起得区分两种场景来介绍。我感觉还不如 zero 标识符一统零值天下。但很可惜,本次提案暂时并没有这个计划。

到此这篇关于Go内置零值标识符zero的文章就介绍到这了,更多相关Go零值内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Go并发同步Mutex典型易错使用场景

    Go并发同步Mutex典型易错使用场景

    这篇文章主要为大家介绍了Go并发同步Mutex典型易错使用场景示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08
  • Golang中漏洞数据库的使用详解

    Golang中漏洞数据库的使用详解

    govulncheck是Golang中的漏洞扫描工具,它强大功能的背后,离不开 Go 漏洞数据库(Go vulnerability database)的支持,所以本文就来为大家详细讲解下 Go 漏洞数据库相关的知识
    2023-09-09
  • Go json反序列化“null“的问题解决

    Go json反序列化“null“的问题解决

    本文主要介绍了Go json反序列化“null“的问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-03-03
  • Go timer如何调度

    Go timer如何调度

    本篇文章剖析下 Go 定时器的相关内容。定时器不管是业务开发,还是基础架构开发,都是绕不过去的存在,由此可见定时器的重要程度,感兴趣的可以了解一下
    2021-06-06
  • Golang中Gin框架中如何定义路由详解

    Golang中Gin框架中如何定义路由详解

    Gin是一个用Go语言编写的Web框架,具有高性能和易于使用的特点,本文将结合实际案例,详细介绍Gin框架的路由用法,有需要的小伙伴可以参考下
    2024-10-10
  • Golang搭建开发环境的图文教程

    Golang搭建开发环境的图文教程

    这篇文章主要介绍了Golang搭建开发环境,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-11-11
  • Go使用path/filepath包处理文件路径的完全指南

    Go使用path/filepath包处理文件路径的完全指南

    本文详细介绍了Go语言中path/filepath包的使用,涵盖了路径处理、拼接、规范化、遍历和匹配等功能,适用于文件系统操作的各种场景,需要的朋友可以参考下
    2026-03-03
  • Go语法糖之‘...’ 的使用实例详解

    Go语法糖之‘...’ 的使用实例详解

    语法糖(Syntactic sugar),也译为糖衣语法,指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用。这篇文章主要给大家介绍Go语法糖之‘...’ 的使用,感兴趣的朋友一起看看吧
    2018-10-10
  • golang获取prometheus数据(prometheus/client_golang包)

    golang获取prometheus数据(prometheus/client_golang包)

    本文主要介绍了使用Go语言的prometheus/client_golang包来获取Prometheus监控数据,具有一定的参考价值,感兴趣的可以了解一下
    2025-03-03
  • Go 第三方库之类型转换问题

    Go 第三方库之类型转换问题

    今天给大家介绍一个第三方库,专门处理类型转换的问题。对Go 第三方库之类型转换问题感兴趣的朋友跟随小编一起看看吧
    2021-08-08

最新评论