《Go语言精进之路》读书笔记 | 使用复合字面值作为初值构造器

Posted COCOgsta

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《Go语言精进之路》读书笔记 | 使用复合字面值作为初值构造器相关的知识,希望对你有一定的参考价值。

书籍来源:《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]int13, 14, 15, 16, 17
sl := []int23, 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 &PipeReaderp, &PipeWriterp

复制代码

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, // Point1.2345, 6.2789
    2.2345, 19.2789, // Point2.2345, 19.2789

复制代码

对于map类型而言,当key或value的类型为复合类型时,我们可以省去key或value中的复合字面量中的类型(这一语法糖在Go 1.5版本中才得以引入):

// Go 1.5之前版本

m := map[Point]string
    Point29.935523, 52.891566:   "Persepolis",
    Point-25.352594, 131.034361: "Uluru",
    Point37.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为&Point29.935523, 52.891566
    "Uluru":      -25.352594, 131.034361, // 相当于value为&Point-25.352594, 131.034361
    "Googleplex": 37.422455, -122.084306, // 相当于value为&Point37.422455, -122.084306


fmt.Println(m2) // map[Googleplex:0xc0000ae050 Persepolis:0xc0000ae030 Uluru:0xc0000ae040]
复制代码

以上是关于《Go语言精进之路》读书笔记 | 使用复合字面值作为初值构造器的主要内容,如果未能解决你的问题,请参考以下文章

《Go语言精进之路》读书笔记 | 使用一致的变量声明形式

《Go语言精进之路》读书笔记 | 使用无类型常量简化代码

《Go语言精进之路》读书笔记 | 使用iota实现枚举常量

《Go语言精进之路》读书笔记 | 使用Go语言原生编码思维来写Go代码

《Go语言精进之路》读书笔记 | 使用Go语言原生编码思维来写Go代码

《Go语言精进之路》读书笔记 | 了解map实现原理并高效使用