大家好我是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结构体的常规的定义和初始化是怎么样的。
常规方式
//定义结构体typelocationstruct{latfloat64longfloat64}//声明变量varloclocation//变量赋值opportunity.lat=-1.9462opportunity.long=354.4734
常规方式这样定义是逐一字段赋值,这样就比较繁琐。
组合字面量方式
//定义结构体typelocationstruct{latfloat64longfloat64}//声明且初始化变量varloclocation={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的顺序无关,如下面示例所示。
如果在结构体中有新增的字段,那么已初始化的变量中该字段为默认类型的零值
//定义结构体typelocationstruct{latfloat64longfloat64}//只给lat字段赋值,long字段默认为float64的零值0loc:=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个数不匹配,则会报错
如果结构体中增加了新字段,则要在该结构体所有初始化的地方都需要补充上该字段的值。
//定义结构体typelocationstruct{latfloat64longfloat64}//不带key的结构体初始化loc:=location{-1.9462,354.4734}//-1.9462赋值给lat字段,354.4734赋值给long字段loc2:=location{-1.9462}//编译时会报错toofewvaluesinstructinitializer
小结
在struct的组合字面量初始化时,推荐使用带key的方式进行初始化,首先,更具有易读性。可以不用关心结构体定义中的字段顺序,每个字段的初始值很明确。其次,比unkey的方式更不容易出错。在结构体中增加了新字段后,已经初始化的代码中不会编译出错,默认是该字段类型的零值。
数组的定义和初始化:常规方式 VS 组合字面量方式
常规方式
varplanets[8]stringplanets[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的定义和初始化
常规方式
vars[]string//定义切片变量s,s为默认零值nils=append(s,"hat","shirt")//往s中增加元素,len(s):2,cap(s):2s:=make([]string,0,10)//定义s,s的默认值不为零值
组合字面量方式
由上面的常规方式可知,首先都是需要先定义切片,然后再往切片中添加元素。接下来我们看下组合字面量方式。
s:=[]string{"hat","shirt"}//定义和初始化一步完成,自动计算切片的容量和长度//orvars=[]string{"hat","shirt"}
map的定义和初始化
常规方式
//通过make函数初始化m:=make(map[string]int,10)m["english"]=99m["math"]=98
组合字面量方式
//定义结构体typelocationstruct{latfloat64longfloat64}//声明且初始化变量varloclocation={lat:-1.9462,long:354.4734}//短语法方式loc2:=location{lat:-1.9462,long:354.4734}//不指定key的方式loc3:=location{-1.9462,354.4734}0
显然,使用组合字面量会比常规方式简单了不少。
小结
组合字面量就是将结构体、数组、切片、map类型的变量定义和初始化放在一起。每次初始化的时候都是新定义一个变量值。尤其在使用struct类型的组合字面量时,可以使用指定key和不带key的方式进行初始化,当然我们推荐使用带key的初始化方式。