原创分享 理解 Go 语言中的组合字面量(Composite Literal)

yudotyang · 2021年09月10日 · 293 次阅读
本帖已被设为精华帖!

大家好我是 Go 学堂的渔夫子。前段时间在工作中,使用 go 的 struct 中的初始化时,由于在未指定的 key 的情况,有几个字段没有赋值,导致编译错误。所以才有了下面这篇文章。

什么是 Composite Literal

首先看下 Go 文档中对 Composite Literal 的定义:

Composite literals construct values for structs, arrays, slices, and maps and create a new value each time they are evaluated. They consist of the type of the literal followed by a brace-bound list of elements. Each element may optionally be preceded by a corresponding key。

翻译成中文大致如下: 组合字面量是为结构体、数组、切片和 map 构造值,并且每次都会创建新值。它们由字面量的类型后紧跟大括号及元素列表。每个元素前面可以选择性的带一个相关 key。

什么意思呢?所谓的组合字面量其实就是把变量的定义和变量的初始化放在一起了

接下来让我们看看结构体、数组、切片和 map 各自的常规方式和组合字面量方式。

结构体的定义和初始化:常规方式 VS 组合字面量方式

让我们看一个 struct 结构体的常规的定义和初始化是怎么样的。

  • 常规方式
//定义结构体
type location struct {
    lat float64
    long float64
}

//声明变量
var loc location

//变量赋值
opportunity.lat = -1.9462
opportunity.long = 354.4734

常规方式这样定义是逐一字段赋值,这样就比较繁琐。

  • 组合字面量方式
//定义结构体
type location struct {
    lat float64
    long float64
}

//声明且初始化变量
var loc location = {lat: -1.9462, long: 354.4734}

//短语法方式
loc2 := location{lat: -1.9462, long:354.4734}

//不指定key的方式
loc3 := location{-1.9462, 354.4734}

在该示例中,我们看到在初始化变量时可以指定结构体的 key,也可以不指定结构体的 key 两种方式。下面我们看看这两种方式各自的特点。

  • 指定结构体 key 的方式:

    • 该方式是按指定的 key 值进行赋值,没指定的 key 的值则默认为结构体中变量的零值
    • key 的顺序无关,如下面示例所示。
    • 如果在结构体中有新增的字段,那么已初始化的变量中该字段为默认类型的零值 ```golang //定义结构体 type location struct { lat float64 long float64 } //只给 lat 字段赋值,long 字段默认为 float64 的零值 0 loc := location{lat: -1.9462} //该实例中 long 字段默认为 0

    //跟 key 的顺序无关 loc1 := location{long:354.4734, lat: -1.9462} loc2 := location{lat: -1.9462, long: 354.4734} fmt.Println(loc1 == loc2) //输出 true

  • 不指定结构体 key 的方式:

    • 该方式是按结构体中定义的 key 顺序依次赋值
    • 结构体中的每个 key 都不必须进行显式赋值
    • 如果在赋值中,元素个数和结构体的 key 个数不匹配,则会报错
    • 如果结构体中增加了新字段,则要在该结构体所有初始化的地方都需要补充上该字段的值。
//定义结构体
type location struct {
    lat float64
    long float64
}
//不带key的结构体初始化
loc := location{-1.9462, 354.4734} //-1.9462赋值给lat字段,354.4734赋值给long字段

loc2 := location{-1.9462} //编译时会报错 too few values in struct initializer
  • 小结 在 struct 的组合字面量初始化时,推荐使用带 key 的方式进行初始化,首先,更具有易读性。可以不用关心结构体定义中的字段顺序,每个字段的初始值很明确。其次,比 unkey 的方式更不容易出错。在结构体中增加了新字段后,已经初始化的代码中不会编译出错,默认是该字段类型的零值。

数组的定义和初始化:常规方式 VS 组合字面量方式

  • 常规方式
var planets [8]string

planets[0] = "Mercury" //水星
planets[1] = "Venus" //金星
planets[2] = "Earth" //地球

在上面的代码中,我们在第 1 行定义了一个 8 个元素大小的字符串数组。然后一个一个的给元素赋值。即数组变量的定义和初始化是分开的。

  • 组合字面量方式
balls := [4]string{"basketball", "football", "Volleyball", "Tennis"}

该示例中,就是将变量 balls 的定义和初始化合并了在一起。

使用组合字面量语法初始化数组时,还可以用三个点"..."来代替数组元素的个数,Go 的编译器在编译时会根据初始化时指定的元素列表来自动计算元素个数。例如:

balls := [...]string{"basketball", "football", "volleyball", "Tennis"}

这样的好处是不用显式的指定元素个数,只有在编译阶段,编译器才会根据列出来的元素列表确定个数。

slice 的定义和初始化

  • 常规方式 ```golang var s [] string //定义切片变量 s,s 为默认零值 nil

s = append(s, "hat", "shirt") //往 s 中增加元素,len(s):2,cap(s):2

s := make([] string, 0, 10) //定义 s,s 的默认值不为零值


- **组合字面量方式**
由上面的常规方式可知,首先都是需要先定义切片,然后再往切片中添加元素。接下来我们看下组合字面量方式。

```golang
s := []string{"hat", "shirt"} //定义和初始化一步完成,自动计算切片的容量和长度
// or
var s = []string{"hat", "shirt"}

map 的定义和初始化

  • 常规方式 ```golang //通过 make 函数初始化 m := make(map[string] int, 10) m["english"] = 99 m["math"] = 98

- **组合字面量方式**
```golang
m := map[string]int {
    "english": 99,
    "math": 98,
}

//组合字面量初始化多维map
m2 := map[string]map[int]string {
    "english": {
        10: "english",
    },
}

显然,使用组合字面量会比常规方式简单了不少。

小结

组合字面量就是将结构体、数组、切片、map 类型的变量定义和初始化放在一起。每次初始化的时候都是新定义一个变量值。尤其在使用 struct 类型的组合字面量时,可以使用指定 key 和不带 key 的方式进行初始化,当然我们推荐使用带 key 的初始化方式。

更多原创文章干货分享,请关注公众号
  • 加微信实战群请加微信(注明:实战群):gocnio
astaxie 将本帖设为了精华贴 09月12日 04:35
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册