复合字面值为结构体、数组、分片和映射构造值,并在每次被求值时创建一个新的值。复合字面值由字面值类型和紧跟着的花括号绑定的元素列表所组成。每个元素可以选择前缀一个对应的键。

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,
}