天天看點

《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]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]
複制代碼