前言
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}