nil map 写入会 panic,读取安全;必须用 make 初始化,空 map({})等价于 make;struct 中 map 字段需显式初始化;nil map 适用于只读且需区分“未设置”场景。

nil map 写入就 panic,这是最常踩的坑
刚写 Go 的人最容易在函数返回 map 或结构体字段里栽跟头:声明了 var m map[string]int,没 make 就直接 m["k"] = v,运行时立刻报 panic: assignment to entry in nil map。这不是编译错误,是 runtime panic,上线后才暴露,非常危险。
- nil map 的零值就是
nil,len(m)返回 0,m == nil为 true - 读取安全:
v := m["missing"]不 panic,只返回int零值(即 0) - 写入、删除、range 遍历都允许,但只有写入(赋值)会 panic
- 典型高危场景:函数返回值是 map 类型,却忘了初始化;或 struct 中 map 字段未在 New 构造函数里
make
empty map 必须用 make 初始化,不是 var 声明就行
空 map 不是“没内容”,而是“已分配底层哈希表、可安全读写”的 map。它和 nil map 表面看都是 len() == 0,但行为天差地别。
- 正确初始化方式只有:
m := make(map[string]int)或带容量的make(map[string]int, 16) -
var m map[string]int→ nil map;m := map[string]int{}→ empty map(语法糖,等价于make) - struct 中嵌套 map 字段,必须显式初始化,例如:
u := &User{Profile: make(map[string]string)} - 注意:
make(map[string]int, 0)和make(map[string]int)行为一致,容量为 0 仅影响初始内存分配,不改变可写性
什么时候可以用 nil map?只读判断场景
nil map 并非一无是处——它轻量、零分配,在某些只读且需区分“未设置”和“已设置为空”的逻辑中反而更清晰。
- API 响应结构中,某个字段是否出现取决于业务逻辑,可用 nil map 表示“该字段未生成”,比空 map 更语义明确
- 配置解析时,
if cfg.Opts == nil可直接判断用户是否配置了该选项组,而len(cfg.Opts) == 0只能说明配置了但没加任何键值 - 切忌混用:不要先用 nil map,中途再
make赋值给同一变量——这容易掩盖初始化遗漏,也破坏调用方对“是否已初始化”的预期
map 作为函数参数传入时,nil 和 empty 的区别会消失
因为 map 是引用类型,传参本质上传的是指针。一旦函数内部对 map 进行了写操作,无论入参原本是 nil 还是 empty,只要它被 make 过,修改就会反映到调用方。
立即学习“go语言免费学习笔记(深入)”;
- 但注意:如果函数接收的是
map[string]int,而你传入一个 nil map,函数内若不做nil检查就直接写,照样 panic - 安全写法是统一处理:
if m == nil { m = make(map[string]int) },或者干脆要求调用方保证非 nil - sync.Map 是并发安全替代品,但它不接受 nil 初始化,必须用字面量或
new(sync.Map),且它的零值本身可安全读写
真正难缠的不是概念,而是那些看似合理却没 make 的一行初始化——比如在 defer 里清空 map、在 for 循环开头重置 map、或把 map 当返回值直接赋值。这些地方,nil 和 empty 的边界一旦模糊,panic 就在下一次部署时等着你。










