Go's Zero Value and Practical Examples

Go 的 Zero Value 與實戰案例

前言

Go 有個概念叫做「Zero Value」,意味著所有變數在宣告時都會被自動初始化為其型別的「Zero Value」,以下是所有型別的範例:

var i int // 0
var f float64 // 0.0
var b bool // false
var s string // ""
var p *int // nil
var s []int
s = append(s, 1, 2, 3) // nil slice append 是合法的,因為它會自動配置底層陣列
var m map[string]int
m["key"] = 1 // panic!nil map 不能寫入,需要已初始化的 struct 必須用 make 初始化

相較於 C 讀取未定義數值的變數會造成安全漏洞或崩潰,或是動態語言如 JavaScript 沒有數值型別初始化的問題,Go 的設計上就自動賦予了確定意義的數值避免出錯與創建即可使用。具體來說在例如使用 json 反射的標籤中 omitempty 也正是運用了 Zero Value 的概念來判斷:

type Response struct {
ID int `json:"id"`
Message string `json:"message,omitempty"` // 空字串時省略
Error string `json:"error,omitempty"` // 空字串時省略
}

reflect 套件有提供 IsZero() 來判斷:

import "reflect"
func isZeroValue(v any) bool {
return reflect.ValueOf(v).IsZero()
}
fmt.Println(isZeroValue(0)) // true
fmt.Println(isZeroValue("")) // true
fmt.Println(isZeroValue(false)) // true
fmt.Println(isZeroValue(42)) // false
fmt.Println(isZeroValue("hello")) // false

實戰可選數值問題

舉例以下設定請求的定義沒有辦法區分「false」和「沒有設定」這兩種不同的狀態

type ConfigRequest struct {
IsEnabled bool `json:"isEnabled"`
}

意味著 {"isEnabled": false}{} 是相同的概念,可能不小心沒提供欄位就錯誤的定義 isEnabled 為 Zero Value。

一種做法是定義 *bool 來表達缺席的數值,如果沒有內容就會自動成為 nil,並且撰寫判斷來明確指定存在的數值:

type ConfigRequest struct {
IsEnabled *bool `json:"isEnabled"`
}
if req.IsEnabled != nil {
config.IsEnabled = *req.IsEnabled
}

延伸閱讀