Go struct 类型的 map 结构体成员不能修改的问题
约 495 字大约 2 分钟
2025-03-19
问题描述
首先,我们先来看这样一段代码
package main
import (
"fmt"
)
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}
func main() {
personTable := map[string]Person{
"1": {Name: "张三", Age: 18},
"2": {Name: "李四", Age: 20},
"3": {Name: "王五", Age: 22},
}
personTable["1"].Age = 19
fmt.Println(persionTable["1"].Age)
}
这段代码的输出结果是多少呢? 答案是: 编译都会不通过, 会收到这样一条报错信息:
编译失败
cannot assign to struct field personTable[1].Age in map.
问题产生的原因
关于golang中map的这种古怪的特性有这样几个观点:
- map作为一个封装好的数据结构,由于它底层可能会由于
数据扩张而进行迁移
,所以拒绝直接寻址,避免产生野指针; - map中的key在不存在的时候,赋值语句其实会进行新的k-v值的插入,所以拒绝直接寻址结构体内的字段,
以防结构体不存在的时候
可能造成的错误; - 这可能和map的
并发不安全性相关
x = y 这种赋值的方式,你必须知道 x 的地址,然后才能把值 y 赋给 x。 但 go 中的 map 的 value 本身是不可寻址的,因为 map 的扩容的时候,可能要做 key/val pair迁移 value 本身地址是会改变的 不支持寻址的话又怎么能赋值呢
问题的解决
迂回方式一:整体更新map的value部分
上述代码修改为如下逻辑:
pTmp := personTable["1"]
pTmp.Age = 19
personTable["1"] = pTmp
迂回方式二:把map的value部分定义为对应类型的指针类型
上述代码修改为:
personTable := map[string]*Person{
"1": {Name: "张三", Age: 18},
"2": {Name: "李四", Age: 20},
"3": {Name: "王五", Age: 22},
}
personTable["1"].Age = 19
## map的实现原理
参考博客: [Golang Map的实现原理](/article/itgspd1c/)