【Golang】不能对map中的值类型结构体成员直接赋值问题:cannot take the address | cannot assign to struct field xxx in map


错误示例

刚用go的时候,意图修改map中的某个结构体的某个成员的值,很多人都会下意识地这样写:

type Temp struct {
	id int32
}

func main() {
	// 声明一个结构体map
	m := make(map[int32]Temp)
	// 给map赋值
	m[1] = Temp{1}
	fmt.Println(m[1])
	// 给map里的结构体的成员赋值
	m[1].id= 2 // GoLand警告:Cannot take the address of 'm[1]'
	fmt.Println(m[1].id)
}

此时编译会抛出错误:cannot assign to struct field m[1].id in map

错误分析

参考stack overflow:将值设置为结构作为映射中的值时,为什么会出现“无法分配”错误?
在这里插入图片描述

golang官方文档中提到:在进行Assignments(分配、赋值)时,左侧操作数必须是:可寻址的、映射索引表达式 或 空白标识符

在这里插入图片描述

映射索引表达式:即对数组或切片的索引进行赋值的形式:arr[0]=1

空白标识符:即下划线 _

可寻址的:文档中对可寻址的的链接如下。简单地说,能用地址运算符&获得地址的,就是可寻址的

在这里插入图片描述

开头的错误示例,其实就是因为赋值符号(=)左侧的m[1].id是 不可寻址的

为什么?↓↓

m[1].id = 2 的意图是把map中key=1对应的value(结构体)的id值修改为2,而实际上这行go代码的正确拆解是:

t := m[1]
t.id = 2

经过拆解,我们首先应该知道:m[1].id = 2的意图是无法实现的。这行代码并不能修改map中的数据,因为map的value是值类型的结构体。传递给t的是拷贝自m[1]的新结构体,和m[1]指向的结构体数据一样,但是内存地址不一样。接下来的t.id = 2,只会修改t的值,和map中的数据没有关系
这就像是下面的例子。结构体是值类型的,如果变量的赋值指向一个值类型,那就是把这个值拷贝了一份给变量

func main() {
	t1 := Temp{1}
	t2 := t1
	t2.id = 2
	fmt.Printf("t1 地址 %p, 数据 %v \n", &t1, t1) // t1 地址 0xc00000a098, 数据 {1} 
	fmt.Printf("t2 地址 %p, 数据 %v \n", &t2, t2) // t2 地址 0xc00000a09c, 数据 {2} 
}

然后回归原题,为什么m[1].id是不可寻址的,从上面的拆解其实很容易解释:拆解中用t来接收了m[1],然后把2赋值给了t.id。但m[1].id = 2这行代码却没有变量接收m[1],这造成的问题是编译器不知道要把2赋值到哪里去,无法寻址。也就抛出了了cannot assign to struct field m[1].id in map

正确的方式

有两种方式
一种是把map中的value(结构体)定义为指针类型。如下

type Temp struct {
	id int32
}

func main() {
	// 声明一个指针结构体map
	m := make(map[int32]*Temp)
	// 给map赋值
	m[1] = &Temp{1}
	fmt.Println(m[1]) // {1}
	// 给map里的指针结构体的成员赋值
	m[1].id= 2
	fmt.Println(m[1].id) // 2
}

另一种是对map中的value进行替换

func main() {
	// 声明一个结构体map
	m := make(map[int32]Temp)
	// 给map赋值
	m[1] = Temp{1}
	fmt.Println(m[1]) // {1}
	// 替换map中的结构体
	m[1] = Temp{2}
	fmt.Println(m[1].id) // 2
}

end

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值