复合字面值为结构体、数组、分片和映射构造值,并在每次被求值时创建一个新的值。复合字面值由字面值类型和紧跟着的花括号绑定的元素列表所组成。每个元素可以选择前缀一个对应的键。
CompositeLit = LiteralType, LiteralValue . LiteralType = StructType | ArrayType | "[", "...", "]", ElementType | SliceType | MapType | TypeName . LiteralValue = "{", [ ElementList, [ "," ] ], "}" . ElementList = KeyedElement, { ",", KeyedElement } . KeyedElement = [ Key, ":" ], Element . Key = FieldName | Expression | LiteralValue . FieldName = identifier . Element = Expression | LiteralValue .
LiteralType 的潜在类型必须是结构体、数组、分片或者映射类型(文法强制执行此约束,除非类型是作为 TypeName 给出的)。元素和键的类型必须是 可分配 到字面值类型所对应的字段、元素和键类型的;这里没有额外的转换。该键被解释为结构体字面值的字段名,数组和分片字面值的索引,映射字面值的键。对于映射字面值而言,索引元素必须要有一个键。给多个元素指定相同的字段名或者不变的键值会出错。查阅 求值顺序 一节获取非常量映射键的信息。
对结构体字面值来说,应用如下规则:
- 键必须为在结构体类型中声明的字段。
- 不包含任何键的元素列表必须对每个结构体字段(字段声明的顺序)列出一个元素。
- 只要一个元素有键,那么每个元素都必须要有键。
- 包含键的元素列表不需要针对每个结构体字段有一个元素。省略的字段会获得一个零值。
- 字面值可以省略元素列表;这样子的字面值相当于其类型的零值。
- 针对属于不同包的结构体的非暴露字段来指定一个元素是错误的。
给定一个声明
type Point3D struct { x, y, z float64 } type Line struct { p, q Point3D }
你可以写
origin := Point3D{} // Point3D 为零值 line := Line{origin, Point3D{y: -4, z: 12.3}} // line.q.x 为零值
对数组和分片字面值来说,应用如下规则:
int
一个复合变量的 寻址 生成了一个到由字面值值初始化的唯一 变量 的指针。
var pointer *Point3D = &Point3D{y: 1000}
注意的是,分片和映射类型的零值不同于同类型的初始化过但为空的值。所以,获取空的分片或映射复合字面值的地址与使用 new 来分配一个新的分片或映射的效果不同。
p1 := &[]int{} // p1 指向一个初始化过的值为 []int{} 长度为 0 的空分片 p2 := new([]int) // p2 指向一个值为 nil 长度为 0 的未初始化过的分片
...
buffer := [10]string{} // len(buffer) == 10 intSet := [6]int{1, 2, 3, 5} // len(intSet) == 6 days := [...]string{"Sat", "Sun"} // len(days) == 2
分片字面值描述了整个底层数组字面值。因此一个分片字面值的长度和容量为其最大元素索引加一。分片字面值的格式为
[]T{x1, x2, … xn}
以及针对应用到数组的分片操作的速记为
tmp := [n]T{x1, x2, … xn} tmp[0 : n]
T
T
*T
&T
这边要多看多熟悉
[...]Point{{1.5, -3.5}, {0, 0}} // 同 [...]Point{Point{1.5, -3.5}, Point{0, 0}} [][]int{{1, 2, 3}, {4, 5}} // 同 [][]int{[]int{1, 2, 3}, []int{4, 5}} [][]Point{{{0, 1}, {1, 2}}} // 同 [][]Point{[]Point{Point{0, 1}, Point{1, 2}}} map[string]Point{"orig": {0, 0}} // 同 map[string]Point{"orig": Point{0, 0}} map[Point]string{{0, 0}: "orig"} // 同 map[Point]string{Point{0, 0}: "orig"} type PPoint *Point [2]*Point{{1.5, -3.5}, {}} // 同 [2]*Point{&Point{1.5, -3.5}, &Point{}} [2]PPoint{{1.5, -3.5}, {}} // 同 [2]PPoint{PPoint(&Point{1.5, -3.5}), PPoint(&Point{})}
当一个使用 LiteralType 的 TypeName 形式的复合字面值表现为一个在 关键字 和 "if" 、 "for" 或 "switch" 语句块的左花括号之间的操作数,并且该复合字面值不被圆括号、方括号或花括号所包围时,会出现一个解析歧义。在这样子一个罕见的情况下,复合字面值的左花括号错误地被解析为语句块的引入。为了解决这样子的歧义,这个复合字段必须在圆括号内。
if x == (T{a,b,c}[i]) { … } if (x == T{a,b,c}[i]) { … }
有效的数组、分片和映射字面值的例子:
// 质数列表 primes := []int{2, 3, 5, 7, 9, 2147483647} // 当 ch 为元音时 vowels[ch] 为真 vowels := [128]bool{'a': true, 'e': true, 'i': true, 'o': true, 'u': true, 'y': true} // 数组 [10]float32{-1, 0, 0, 0, -0.1, -0.1, 0, 0, 0, -1} filter := [10]float32{-1, 4: -0.1, -0.1, 9: -1} // 十二平均律以 Hz 为单位的频率(A4 = 440Hz) noteFrequency := map[string]float32{ "C0": 16.35, "D0": 18.35, "E0": 20.60, "F0": 21.83, "G0": 24.50, "A0": 27.50, "B0": 30.87, }