结构体变量不能赋 nil,因struct是值类型且无nil状态;需用*person等指针类型表达“可空”语义,其零值为nil,支持==nil比较与赋nil操作。

结构体变量不能赋 nil,编译会直接报错
Go 中 struct 是值类型,没有 nil 状态。你写 var p Person 或 p := Person{},得到的永远是一个合法内存布局的实例,哪怕所有字段都是零值。
一旦尝试 p = nil,编译器立刻报错:cannot use nil as Person value in assignment。
常见错误现象:
- 把 Java/C++ 习惯带进来,以为结构体可以“置空”
- 在 JSON 反序列化后想用
if p == nil判断是否有效,结果编译失败 - 误以为
Person{}和nil是等价语义,导致逻辑分支漏判
需要“可空”语义?必须用 *Person 而非 Person
真正能表达“未设置/已清除”意图的,是结构体指针类型。*Person 的零值就是 nil,它支持 == nil 比较,也支持赋 nil。
实操建议:
- 定义变量时就声明为指针:
var p *Person,而非var p Person - 初始化用
&Person{...},不是Person{...} - 清空时直接
p = nil,安全且语义清晰 - 调用方法前检查:
if p != nil { p.DoSomething() },避免 panic
注意:即使 *Person 方法接收者是值类型(func (p Person) Method()),传入 nil 指针也会 panic——因为解引用发生在调用前,Go 会先 dereference 再复制,所以务必确保指针非 nil 才调用。
结构体字段里嵌指针,才是控制“字段级可空”的关键
结构体本身不可为空,但它的字段可以是可空的。比如 ID *big.Int、Tags *[]string、Config *Config —— 这些字段天然支持 nil,且反序列化时能区分“字段缺失”和“字段显式设为空数组/空对象”。
典型使用场景:
- API 请求体中,某些字段可选;JSON 解析后,
*int字段为nil表示未提供,&42表示明确传了 42 - 数据库 ORM 中,用
*time.Time区分“时间未设置”和“时间为 Unix 零点” - 配置合并逻辑中,只覆盖非
nil字段,保留原值
别写 Tags []string 然后靠 len(Tags) == 0 判断是否提供——这无法区分 [] 和缺失字段;改用 Tags *[]string,再用 Tags != nil 判断。
json.Unmarshal 时 struct 零值与 nil 指针的行为差异
JSON 反序列化对结构体值类型和指针类型处理完全不同:
-
var p Person+json.Unmarshal(b, &p):字段未出现在 JSON 中 → 保持零值(Name=="",Age==0) -
var p *Person+json.Unmarshal(b, &p):JSON 为空或 null →p保持nil;JSON 有内容 →p指向新分配的实例 - 字段为
*string:JSON 中该字段缺失 → 字段保持nil;JSON 中为null→ 字段也设为nil;JSON 中为"abc"→ 字段指向"abc"
容易踩的坑:
- 用
Person{}接收 JSON,却期望通过字段是否为零值判断客户端是否传了该字段——不可靠,因为零值可能是默认值,也可能是客户端真的传了 0/"" - 忘记对指针字段做非空检查就直接解引用,比如
if p.Name != nil { fmt.Println(*p.Name) },否则 panic
真正要区分“未提供”和“提供了零值”,只能靠指针字段 + 显式判 != nil,没有捷径。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











