书籍来源:《Go语言精进之路:从新手到高手的编程思想、方法和技巧》
一边学习一边整理读书笔记,并与大家分享,侵权即删,谢谢支持!
附上汇总贴:Go语言精进自学系列 | 汇总_COCOgsta的博客-CSDN博客
有时有必要为变量赋予适当的初值以保证其后续以正确的状态参与业务流程计算,尤其是Go语言中的一些复合类型的变量。
Go语言中的复合类型包括结构体、数组、切片和map。对于复合类型变量,最常见的值构造方式就是对其内部元素进行逐个赋值,比如:
var s myStruct
s.name = "tony"
s.age = 23
var a [5]int
a[0] = 13
a[1] = 14
...
a[4] = 17
sl := make([]int, 5, 5)
sl[0] = 23
sl[1] = 24
...
sl[4] = 27
m := make(map[int]string)
m[1] = "hello"
m[2] = "gopher"
m[3] = "!"
但这样的值构造方式让代码显得有些烦琐,Go提供的复合字面值语法可以作为复合类型变量的初值构造器。上述代码可以改写成下面这样:
s := myStruct{"tony", 23}
a := [5]int{13, 14, 15, 16, 17}
sl := []int{23, 24, 25, 26, 27}
m := map[int]string {1:"hello", 2:"gopher", 3:"!"}
下面来分别看看复合字面值对于不同复合类型的高级用法。
12.1 结构体复合字面值
Go推荐使用field:value的复合字面值形式对struct类型变量进行值构造,这种值构造方式可以降低结构体类型使用者与结构体类型设计者之间的耦合。比如:
// $GOROOT/src/net/http/transport.go
var DefaultTransport RoundTripper = &Transport{
Proxy: ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
DualStack: true,
}).DialContext,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
// $GOROOT/src/io/pipe.go
type pipe struct {
wrMu sync.Mutex
wrCh chan []byte
rdCh chan int
once sync.Once
done chan struct{}
rerr onceError
werr onceError
}
func Pipe() (*PipeReader, *PipeWriter) {
p := &pipe{
wrCh: make(chan []byte),
rdCh: make(chan int),
done: make(chan struct{}),
}
return &PipeReader{p}, &PipeWriter{p}
}
field:value形式字面值中的字段可以以任意次序出现,未显式出现在字面值的结构体中的字段将采用其对应类型的零值。
以上面的pipe类型为例,Pipe函数在对pipe类型变量进行初值构造时仅对wrCh、rdCh和done进行了field:value形式的显式赋值,这样pipe结构体中的其他变量的值将为其类型的初值,如wrMu。
12.2 数组/切片复合字面值
与结构体类型不同,数组/切片使用下标(index)作为field:value形式中的field:
numbers := [256]int{'a': 8, 'b': 7, 'c': 4, 'd': 3, 'e': 2, 'y': 1, 'x': 5}
// [10]float{-1, 0, 0, 0, -0.1, -0.1, 0, 0.1, 0, -1}
fnumbers := [...]float{-1, 4: -0.1, -0.1, 7:0.1, 9: -1}
数组/切片采用index:value为其构造初值,主要应用在少数场合,比如为非连续(稀疏)元素构造初值(如上面示例中的numbers、fnumbers)。
12.3 map复合字面值
和结构体、数组/切片相比,map类型变量使用复合字面值作为初值构造器就显得自然许多,因为map类型具有原生的key:value构造形式:
// $GOROOT/src/time/format.go
var unitMap = map[string]int64{
"ns": int64(Nanosecond),
"us": int64(Microsecond),
"µs": int64(Microsecond), // U+00B5 = 微符号
"μs": int64(Microsecond), // U+03BC = 希腊字母μ
"ms": int64(Millisecond),
...
}
// $GOROOT/src/net/http/server.go
var stateName = map[ConnState]string{
StateNew: "new",
StateActive: "active",
StateIdle: "idle",
StateHijacked: "hijacked",
StateClosed: "closed",
}
对于数组/切片类型而言,当元素为复合类型时,可以省去元素复合字面量中的类型,比如:
type Point struct {
x float64
y float64
}
sl := []Point{
{1.2345, 6.2789}, // Point{1.2345, 6.2789}
{2.2345, 19.2789}, // Point{2.2345, 19.2789}
}
对于map类型而言,当key或value的类型为复合类型时,我们可以省去key或value中的复合字面量中的类型(这一语法糖在Go 1.5版本中才得以引入):
// Go 1.5之前版本
m := map[Point]string{
Point{29.935523, 52.891566}: "Persepolis",
Point{-25.352594, 131.034361}: "Uluru",
Point{37.422455, -122.084306}: "Googleplex",
}
// Go 1.5及之后版本
m := map[Point]string{
{29.935523, 52.891566}: "Persepolis",
{-25.352594, 131.034361}: "Uluru",
{37.422455, -122.084306}: "Googleplex",
}
m1 := map[string]Point{
"Persepolis": {29.935523, 52.891566},
"Uluru": {-25.352594, 131.034361},
"Googleplex": {37.422455, -122.084306},
}
对于key或value为指针类型的情况,也可以省略“&T”:
m2 := map[string]*Point{
"Persepolis": {29.935523, 52.891566}, // 相当于value为&Point{29.935523, 52.891566}
"Uluru": {-25.352594, 131.034361}, // 相当于value为&Point{-25.352594, 131.034361}
"Googleplex": {37.422455, -122.084306}, // 相当于value为&Point{37.422455, -122.084306}
}
fmt.Println(m2) // map[Googleplex:0xc0000ae050 Persepolis:0xc0000ae030 Uluru:0xc0000ae040]