前言
Go 有個概念叫做「Zero Value」,意味著所有變數在宣告時都會被自動初始化為其型別的「Zero Value」,以下是所有型別的範例:
var i int // 0var f float64 // 0.0var b bool // falsevar s string // ""var p *int // nil
var s []ints = append(s, 1, 2, 3) // nil slice append 是合法的,因為它會自動配置底層陣列
var m map[string]intm["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)) // truefmt.Println(isZeroValue("")) // truefmt.Println(isZeroValue(false)) // truefmt.Println(isZeroValue(42)) // falsefmt.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}