编程书说的 “Go 程序员应该让聚合类型的零值也具有意义” 是在讲什么

在《Go 语言编程》这本书和很多其他 Go 编程教程中很多都提到过 “Go 程序员应该让一些聚合类型的零值也具有意义” 的概念,我们这篇文章主要说一下有意义的零值这个话题。

在 Go 中声明变量时如果初始化表达式被省略:

var 变量名字 类型 = 表达式

那么将用零值初始化变量。

以下是 Go 官方的语言参考对零值初始化机制的解释:

When storage is allocated for a variable, either through a declaration or a call of new, or when a new value is created, either through a composite literal or a call of make, and no explicit initialization is provided, the variable or value is given a default value. Each element of such a variable or value is set to the zero value for its type: false for booleans, 0 for numeric types, "" for strings, and nil for pointers, functions, interfaces, slices, channels, and maps. This initialization is done recursively, so for instance each element of an array of structs will have its fields zeroed if no value is specified.

当通过变量声明、调用 new 函数或者是通过符合字面量 ([] string {}, structType {} 等形式)、调用 make 函数创建新值并且未显式的提供初始化时,变量或者值将被赋予默认值。变量或者值的每个元素将被赋予其类型的零值:布尔值为 false,数字类型为 0,字符串为 “”,指针,函数,接口,切片,通道和映射为 nil。该初始化是递归完成的,因此,例如,未指定任何值,一个结构体数组的每个元素的字段都将设置为字段类型的零值。

Go 始终将值设置为已知默认值的特性对于程序的安全性和正确性很重要,也使 Go 程序更简单,更紧凑。这就是 Go 程序员在说 “给你的结构体一个有用的零值” 时谈论的内容。

下面是一个使用 sync.Mutex 的示例,该示例设计为无需显式初始化即可使用。 sync.Mutex 包含两个未导出的整数字段:

type Mutex struct {
    state int32
    sema  uint32
}

由于零值机制的存在,每当声明 sync.Mutex 时,这些字段将被设置为 0。

package main

import "sync"

type MyInt struct {
        mu sync.Mutex
        val int
}

func main() {
        var i MyInt

        // i.mu is usable without explicit initialisation.
        i.mu.Lock()      
        i.val++
        i.mu.Unlock()
}

有用的零值的类型的另一个示例是 bytes.Buffer。你可以在声明了一个 bytes.Buffer 类型的变量后,无需显式初始化即可开始读取或写入。

package main

import "bytes"
import "io"
import "os"

func main() {
        var b bytes.Buffer
        b.Write([]byte("Hello world"))
        io.Copy(os.Stdout, &b)
}

切片类型的零值为 nil。这意味着你无需显式创建切片,只需声明它即可。

package main

import "fmt"
import "strings"

func main() {
        // s := make([]string, 0)
        // s := []string{}
        var s []string

        s = append(s, "Hello")
        s = append(s, "world")
        fmt.Println(strings.Join(s, " "))
}

注意:var s [] string 与它上面的两条注释行相似,但是不相同。可以通过程序检测出 nil 切片值与具有零长度的切片值之间的差别。以下代码将输出 false。

package main

import "fmt"
import "reflect"

func main() {
        var s1 = []string{}
        var s2 []string
        fmt.Println(reflect.DeepEqual(s1, s2))
}

对于 nil 指针来说,你可以让你的程序允许在具有 nil 值的类型上调用方法。这可以用来简单地为方法提供有意义的默认返回值。比如下面的程序在 nil 指针上调用 Path 方法是返回了 /usr/home ,示例为了好理解只是简单输出了一下调用结果,但是在很多比示例更复杂的功能方法来说这比直接返回 string 的零值空字符对程序更有意义。

package main

import "fmt"

type Config struct {
        path string
}

func (c *Config) Path() string {
        if c == nil {
                return "/usr/home"
        }
        return c.path
}

func main() {
        var c1 *Config
        var c2 = &Config{
                path: "/export",
        }
        fmt.Println(c1.Path(), c2.Path())
}
Image placeholder
ferrior30
未设置
  0人点赞

没有讨论,发表一下自己的看法吧

推荐文章
波音737事故反思:该让数据“接管”生命控制权吗?

大数据文摘出品作者:林安安、蒋宝尚2018年10月29日,一架载有189名乘客和机组人员的印尼狮航波音737MAX8客机,在起飞13分钟后失联,随后被确认在西爪哇附近海域坠毁,机上人员全部遇难。截止到

第 10 节:复合类型 1.4 冒泡排序与数组去重

04冒泡排序packagemain import"fmt" funcmain(){ vararr[10]int=[10]int{9,1,5,6,8,2,10,7,4,3} //外层执行一次内层执

第 10 节:复合类型 1.5 二维数组定义和使用

06二维数组定义和使用packagemain import"fmt" funcmain0701(){ //vararr[10]int一维数组 vararr[2][3]int arr[0][1

第 10 节:复合类型 2: 切片

2:切片07切片定义和使用切片:切片与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大,所以可以将切片理解成“动态数组”,但是,它不是数组。packagemain impor

第 10 节:复合类型 1:数组

1:数组01数组定义和使用packagemain import"fmt" funcmain0101(){ //数组定义 //数组是一系列相同数据类型在内存中有序存储的数据集合 //var数组名[

第 10 节:复合类型 小练习!

练习1.数组作为函数参数packagemain import"fmt" funcswap(aint,bint){ a,b=b,a } funcmain0101(){ a:=10 b:=20 //

第 10 节:复合类型-5. 指针 -- 指针与指针变量

5.指针--不容易懂重点01指针定义和使用--不容易懂重点现在已经知道怎样获取变量在内存中的地址,但是如果想将获取的地址进行保存,应该怎样做呢?\ 可以通过指针变量来存储,所谓的指针变量:就是用来存储

第 10 节:复合类型-5. 指针 -- 数组指针与指针数组的辅助理解

5数组指针与指针数组的辅助理解数组指针(也称行指针)定义int(*p)[n];\ ()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p

第 10 节:复合类型- 6.指针和切片

6.指针和切片packagemain import"fmt" funcmain0701(){ slice:=[]int{1,2,3,4,5,6,7,8,9,10} //指针和切片建立关系 p:=

第 10 节:复合类型-5. 指针 -- 数组指针与指针数组

3.数组指针前面在讲解数组的时候,我们用数组作为函数参数,但是数组作为参数进行传递是值传递,如果想引用传递,可以使用数组指针。具体使用方式如下:\ 定义一个数组,作为函数Swap的实参进行传递,但是这

第 10 节:复合类型-5. 指针 -- 指针与指针变量 7. 结构体指针变量

7.结构体指针变量我们前面定义了指针指向了数组,解决了数组引用传递的问题。那么指针是否可以指向结构体,也能够解决结构体引用传递的问题呢?完全可以。\ 下面我们先来看一下,结构体指针变量的定义:\\ 也

一篇文章读懂“GAN”——生成式对抗网络

机器学习是一个不断发展的领域,因此对于很多人来说,时刻跟踪这一领域的最新进展是很难的。GAN(生成式对抗网络)是最近引起广泛关注的新兴领域之一,为了让大家能够更好地跟上技术发展的脚步,我们安排了一个简

面试题:如何理解 Linux 的零拷贝技术?

本文讲解Linux的零拷贝技术,云计算是一门很庞大的技术学科,融合了很多技术,Linux算是比较基础的技术,所以,学好Linux对于云计算的学习会有比较大的帮助。本文借鉴并总结了几种比较常见的Linu

netty中的零拷贝

Netty的零拷贝体现在三个方面:buffer层面对于ByteBuf,Netty提供了多种实现:a.HeapByteBuf:直接在堆内存分配b.DirectByteBuf:直接在内存区域分配而不是堆内

5位女性程序员的自白:计算机不撒谎;女程序员的代码一样也很棒

谁说这个领域就是男性的天下偏见本身就是一种带标签的想法她们的世界里只信奉“computer never lies”她们认为代码漂亮比发型漂亮更重要她们到底是谁?谷悦是喜欢简单、纯粹工作的气质女神,八年

InnoDB到底支不支持哈希索引,为啥不同的人说的不一样?

继续回答水友提问(最近问MySQL的多):沈老师,我在网上看到不同的资料,有的说InnoDB支持哈希索引,有的说不支持,到底哪个是正确的呢?对于InnoDB的哈希索引,确切的应该这么说:(1)Inno

当中小企业决定上云,真的像你们说的那么简单吗?

题图:fromZoommy四季度历来是一年中最忙碌的时期,辛苦了一年,各项工作都在收官,千头万绪、环环相扣,再加上绩效考核的开展,不但烧脑,而且还烧心。同时,最后一个季度又肩负着为来年开局而打基础的艰

Go编程语言教程_2.2. Go中的数据类型

数据类型指定有效的Go变量可以保存的数据类型。在Go语言中,类型分为以下四类: 基本类型:数字,字符串和布尔值属于此类别。 聚集类型:数组和结构属于此类别。 引用类型:指针,切片,地图,函数和通道属于

Go编程语言教程_1.0. Go编程语言(简介)

介绍 Go是一种过程编程语言。它由Google的RobertGriesemer,RobPike和KenThompson于2007年开发,但于2009年作为一种开放源代码编程语言发布。程序通过使用软件包

Go编程语言教程_1.6. Go和Python编程语言之间的区别

Golang是一种过程编程语言。它由Google的RobertGriesemer,RobPike和KenThompson于2007年开发,但于2009年作为一种开放源代码编程语言发布。程序通过使用软件

可视化编程是否真的没有未来?程序员:它有“七宗罪”

今天想聊聊可视化编程(visual-programming)的未来发展,喂喂,咱们这儿还没开始,各位大佬先别急着走啊您……确实,可视化这个概念跟任何技术并称,都是技术前沿、下一个风口、万亿市场的代名词

码龄超过20年,依然对生活和编程充满激情,这是三位70后“老”程序员的故事

大数据文摘出品作者:周素云、张秋玥加班996,生病ICU。这是一句最近搅乱了很多程序员平静生活,也让所有的“社畜”认真反思人生的话题。但是,让程序员们真正感到焦虑的其实并不只是工作的压力,更多的是对未

三种类型的物联网平台分析

企业依靠其物联网平台提供许多服务。其中最重要的是分析。通俗地说,物联网分析是一门科学和艺术,它试图在连接资产生成的海量数据中找到模式。MachNation的物联网平台测试实验室给出更详尽地定义,分析是

数据类型的分类

点击下方截图可插入当前视频播放画面,了解更多Mackdown语法可以点击上方?图标

jquery中有哪几种类型的选择器?

jQuery选择器一、基本选择器基本选择器是jQuery中最常用也是最简单的选择器,它通过元素的id、class和标签名等来查找DOM元素。1、ID选择器#id描述:根据给定的id匹配一个元素,返回单