Go 语言中的复合类型包括结构体、数组、切片和 map。

Go 提供的复合字面值(composite literals)语法可以作为复合类型变量的初值构造器。使用复合字面值上述代码可以改写成下面这样:

  1. s := myStruct {"tony", 23}
  2. a := [5]int{13, 14, 15, 16, 17}
  3. sl := []int{23, 24, 25, 26, 27}
  4. m := map[int]string {1:"hello", 2:"gopher", 3:"!"}

1. 结构体复合字面值

Go 推荐使用 “field:value” 格式的复合字面值形式对 struct 类型变量进行值构造,这种值构造方式可以降低结构体类型使用者与结构体类型设计者之间的耦合。这也是 Go 语言的惯用法,在 Go 标准库中,通过”field:value”格式复合字面值进行结构体类型变量初值构造的例子比比皆是:

  1. // $GOROOT/src/net/http/transport.go
  2. var DefaultTransport RoundTripper = &Transport{
  3. Proxy: ProxyFromEnvironment,
  4. DialContext: (&net.Dialer{
  5. Timeout: 30 * time.Second,
  6. KeepAlive: 30 * time.Second,
  7. DualStack: true,
  8. }).DialContext,
  9. MaxIdleConns: 100,
  10. IdleConnTimeout: 90 * time.Second,
  11. TLSHandshakeTimeout: 10 * time.Second,
  12. ExpectContinueTimeout: 1 * time.Second,
  13. }
  14. // $GOROOT/src/io/pipe.go
  15. type pipe struct {
  16. wrMu sync.Mutex // Serializes Write operations
  17. wrCh chan []byte
  18. rdCh chan int
  19. once sync.Once // Protects closing done
  20. done chan struct{}
  21. rerr atomicError
  22. werr atomicError
  23. }
  24. func Pipe() (*PipeReader, *PipeWriter) {
  25. p := &pipe{
  26. wrCh: make(chan []byte),
  27. rdCh: make(chan int),
  28. done: make(chan struct{}),
  29. }
  30. return &PipeReader{p}, &PipeWriter{p}
  31. }

2. 数组/切片复合字面值

和结构体类型不同,数组/切片使用下标(index)作为 “field:value” 形式中 “field”,从而实现数组/切片初始元素值的高级构造形式:

  1. numbers := [256]int{'a': 8, 'b': 7, 'c': 4, 'd': 3, 'e': 2, 'y': 1, 'x': 5}
  2. // [10]float{-1, 0, 0, 0, -0.1, -0.1, 0, 0.1, 0, -1}
  3. fnumbers := [...]float{-1, 4: -0.1, -0.1, 7:0.1, 9: -1}
  4. // $GOROOT/src/sort/search_test.go
  5. var data = []int{0: -10, 1: -5, 2: 0, 3: 1, 4: 2, 5: 3, 6: 5, 7: 7, 8: 11, 9: 100, 10: 100, 11: 100, 12: 1000, 13: 10000}
  6. var sdata = []string{0: "f", 1: "foo", 2: "foobar", 3: "x"}

3. map 复合字面值

和结构体、数组/切片相比,map 类型变量使用复合字面值作为初值构造器就显得自然了许多,因为 map 类型具有原生的 “key:value” 抽象形式:

  1. // $GOROOT/src/time/format.go
  2. var unitMap = map[string]int64{
  3. "ns": int64(Nanosecond),
  4. "us": int64(Microsecond),
  5. "µs": int64(Microsecond), // U+00B5 = micro symbol
  6. "μs": int64(Microsecond), // U+03BC = Greek letter mu
  7. "ms": int64(Millisecond),
  8. "s": int64(Second),
  9. "m": int64(Minute),
  10. "h": int64(Hour),
  11. }
  12. // $GOROOT/src/net/http/server.go
  13. type ConnState int
  14. const (
  15. StateNew ConnState = iota
  16. StateActive
  17. StateIdle
  18. StateHijacked
  19. StateClosed
  20. )
  21. var stateName = map[ConnState]string{
  22. StateNew: "new",
  23. StateActive: "active",
  24. StateIdle: "idle",
  25. StateHijacked: "hijacked",
  26. StateClosed: "closed",
  27. }

对于数组/切片类型而言,当元素的类型为复合类型时,我们可以省去元素复合字面量中的类型,比如:

  1. type Point struct {
  2. x float64
  3. y float64
  4. }
  5. sl := []Point{
  6. {1.2345, 6.2789}, // Point{1.2345, 6.2789}
  7. {2.2345, 19.2789}, // Point{2.2345, 19.2789}
  8. }

对于 map 类型而言,这一优化在 Go 1.5 中才得以引入。引入后,当 key 或 value 的类型为复合类型时,我们可以省去 key 或 value 中的复合字面量中的类型:

  1. // Go 1.5版本之前:
  2. m := map[Point]string{
  3. Point{29.935523, 52.891566}: "Persepolis",
  4. Point{-25.352594, 131.034361}: "Uluru",
  5. Point{37.422455, -122.084306}: "Googleplex",
  6. }
  7. vs.
  8. // Go 1.5版本及之后
  9. m := map[Point]string{
  10. {29.935523, 52.891566}: "Persepolis",
  11. {-25.352594, 131.034361}: "Uluru",
  12. {37.422455, -122.084306}: "Googleplex",
  13. }
  14. m1 := map[string]Point{
  15. "Persepolis": {29.935523, 52.891566},
  16. "Uluru": {-25.352594, 131.034361},
  17. "Googleplex": {37.422455, -122.084306},
  18. }

对于 key 或 value 为指针类型的情况,我们也可以省略 “&T”:

  1. m2 := map[string]*Point{
  2. "Persepolis": {29.935523, 52.891566}, // &Point {29.935523, 52.891566}
  3. "Uluru": {-25.352594, 131.034361}, // &Point{-25.352594, 131.034361}
  4. "Googleplex": {37.422455, -122.084306}, // &Point{37.422455, -122.084306}
  5. }
  6. fmt.Println(m2) // map[Googleplex:0xc0000ae050 Persepolis:0xc0000ae030 Uluru:0xc0000ae040]