理解 Go 語言中的組合字面量(Composite Literal)

yudotyang發表於2021-09-10

大家好我是 Go 學堂的漁夫子。前段時間在工作中,使用 go 的 struct 中的初始化時,由於在未指定的 key 的情況,有幾個欄位沒有賦值,導致編譯錯誤。所以才有了下面這篇文章。

什麼是 Composite Literal

首先看下 Go 文件中對 Composite Literal 的定義:

Composite literals construct values for structs, arrays, slices, and maps and create a new value each time they are evaluated. They consist of the type of the literal followed by a brace-bound list of elements. Each element may optionally be preceded by a corresponding key。

翻譯成中文大致如下: 組合字面量是為結構體、陣列、切片和 map 構造值,並且每次都會建立新值。它們由字面量的型別後緊跟大括號及元素列表。每個元素前面可以選擇性的帶一個相關 key。

什麼意思呢?所謂的組合字面量其實就是把變數的定義和變數的初始化放在一起了

接下來讓我們看看結構體、陣列、切片和 map 各自的常規方式和組合字面量方式。

結構體的定義和初始化:常規方式 VS 組合字面量方式

讓我們看一個 struct 結構體的常規的定義和初始化是怎麼樣的。

  • 常規方式
//定義結構體
type location struct {
    lat float64
    long float64
}

//宣告變數
var loc location

//變數賦值
opportunity.lat = -1.9462
opportunity.long = 354.4734

常規方式這樣定義是逐一欄位賦值,這樣就比較繁瑣。

  • 組合字面量方式
//定義結構體
type location struct {
    lat float64
    long float64
}

//宣告且初始化變數
var loc location = {lat: -1.9462, long: 354.4734}

//短語法方式
loc2 := location{lat: -1.9462, long:354.4734}

//不指定key的方式
loc3 := location{-1.9462, 354.4734}

在該示例中,我們看到在初始化變數時可以指定結構體的 key,也可以不指定結構體的 key 兩種方式。下面我們看看這兩種方式各自的特點。

  • 指定結構體 key 的方式:

    • 該方式是按指定的 key 值進行賦值,沒指定的 key 的值則預設為結構體中變數的零值
    • key 的順序無關,如下面示例所示。
    • 如果在結構體中有新增的欄位,那麼已初始化的變數中該欄位為預設型別的零值 ```golang //定義結構體 type location struct { lat float64 long float64 } //只給 lat 欄位賦值,long 欄位預設為 float64 的零值 0 loc := location{lat: -1.9462} //該例項中 long 欄位預設為 0

    //跟 key 的順序無關 loc1 := location{long:354.4734, lat: -1.9462} loc2 := location{lat: -1.9462, long: 354.4734} fmt.Println(loc1 == loc2) //輸出 true

  • 不指定結構體 key 的方式:

    • 該方式是按結構體中定義的 key 順序依次賦值
    • 結構體中的每個 key 都不必須進行顯式賦值
    • 如果在賦值中,元素個數和結構體的 key 個數不匹配,則會報錯
    • 如果結構體中增加了新欄位,則要在該結構體所有初始化的地方都需要補充上該欄位的值。
//定義結構體
type location struct {
    lat float64
    long float64
}
//不帶key的結構體初始化
loc := location{-1.9462, 354.4734} //-1.9462賦值給lat欄位,354.4734賦值給long欄位

loc2 := location{-1.9462} //編譯時會報錯 too few values in struct initializer
  • 小結 在 struct 的組合字面量初始化時,推薦使用帶 key 的方式進行初始化,首先,更具有易讀性。可以不用關心結構體定義中的欄位順序,每個欄位的初始值很明確。其次,比 unkey 的方式更不容易出錯。在結構體中增加了新欄位後,已經初始化的程式碼中不會編譯出錯,預設是該欄位型別的零值。

陣列的定義和初始化:常規方式 VS 組合字面量方式

  • 常規方式
var planets [8]string

planets[0] = "Mercury" //水星
planets[1] = "Venus" //金星
planets[2] = "Earth" //地球

在上面的程式碼中,我們在第 1 行定義了一個 8 個元素大小的字串陣列。然後一個一個的給元素賦值。即陣列變數的定義和初始化是分開的。

  • 組合字面量方式
balls := [4]string{"basketball", "football", "Volleyball", "Tennis"}

該示例中,就是將變數 balls 的定義和初始化合並了在一起。

使用組合字面量語法初始化陣列時,還可以用三個點"..."來代替陣列元素的個數,Go 的編譯器在編譯時會根據初始化時指定的元素列表來自動計算元素個數。例如:

balls := [...]string{"basketball", "football", "volleyball", "Tennis"}

這樣的好處是不用顯式的指定元素個數,只有在編譯階段,編譯器才會根據列出來的元素列表確定個數。

slice 的定義和初始化

  • 常規方式 ```golang var s [] string //定義切片變數 s,s 為預設零值 nil

s = append(s, "hat", "shirt") //往 s 中增加元素,len(s):2,cap(s):2

s := make([] string, 0, 10) //定義 s,s 的預設值不為零值


- **組合字面量方式**
由上面的常規方式可知,首先都是需要先定義切片,然後再往切片中新增元素。接下來我們看下組合字面量方式。

```golang
s := []string{"hat", "shirt"} //定義和初始化一步完成,自動計算切片的容量和長度
// or
var s = []string{"hat", "shirt"}

map 的定義和初始化

  • 常規方式 ```golang //通過 make 函式初始化 m := make(map[string] int, 10) m["english"] = 99 m["math"] = 98

- **組合字面量方式**
```golang
m := map[string]int {
    "english": 99,
    "math": 98,
}

//組合字面量初始化多維map
m2 := map[string]map[int]string {
    "english": {
        10: "english",
    },
}

顯然,使用組合字面量會比常規方式簡單了不少。

小結

組合字面量就是將結構體、陣列、切片、map 型別的變數定義和初始化放在一起。每次初始化的時候都是新定義一個變數值。尤其在使用 struct 型別的組合字面量時,可以使用指定 key 和不帶 key 的方式進行初始化,當然我們推薦使用帶 key 的初始化方式。

更多原創文章乾貨分享,請關注公眾號
  • 理解 Go 語言中的組合字面量(Composite Literal)
  • 加微信實戰群請加微信(註明:實戰群):gocnio

相關文章