map存储struct结构体常见的坑
1 使用指针作为方法的 receiver
package main
import (
"fmt"
)
type data struct {
name string
}
type printer interface {
print()
}
// 这里不能使用指针
func (p *data) print() {
fmt.Println("name: ", p.name)
}
/*
//解决方法一: 正确的语法, 不能使用指针
func (p data) print() {
fmt.Println("name: ", p.name)
}
*/
func main() {
d1 := data{"one"}
d1.print() // d1 变量可寻址,可直接调用指针 receiver 的方法
/*
var in printer = data{"two"}
in.print() // Error: 类型不匹配
*/
m := map[string]data{
"x": data{"three"},
}
// 对于 map 存储struct的数据类型, 不能使用指针
m["x"].print() //Error: m["x"] 是不可寻址的 // 变动频繁
// 解决方法二:
r := m["x"]
r.print()
}
/*
cannot use data literal (type data) as type printer in assignment:
data does not implement printer (print method has pointer receiver)
cannot call pointer method on m["x"]
cannot take the address of m["x"]
只要值是可寻址的, 就可以在值上直接调用指针方法。即是对一个方法, 它的 receiver 是指针就足矣。
但不是所有值都是可寻址的, 比如 map 类型的元素、通过 interface 引用的变量:
*/
2 更新 map 字段的值
package main
type data struct {
name string
}
func main() {
m := map[string]data{
"x": {"Tom"},
}
m["x"].name = "Jerry" // 无法直接更新 struct 的字段值
}
/*
cannot assign to struct field m[“x”].name in map
因为 map 中的元素是不可寻址的。需区分开的是,slice 的元素可寻址:
*/
更新 map 中 struct 元素的字段值, 有 2 个方法:
(1) 使用局部变量
package main
import (
"fmt"
)
// 提取整个 struct 到局部变量中, 修改字段值后再整个赋值
type data struct {
name string
}
func main() {
m := map[string]data{
"x": {"Tom"},
}
r := m["x"]
r.name = "Jerry"
m["x"] = r
fmt.Println(m) // map[x:{Jerry}]
}
(2) 使用指向元素的 map 指针
package main
import (
"fmt"
)
type data struct {
name string
}
func main() {
m := map[string]*data{
"x": {"Tom"},
}
m["x"].name = "Jerry" // 直接修改 m["x"] 中的字段
fmt.Println(m["x"]) // &{Jerry}
/*
// 但是要注意下边这种误用:
m["z"].name = "what" // panic: runtime error: invalid memory address or nil pointer dereference
fmt.Println(m["x"])
*/
}
综合: slice 与 map 存储 struct的区别:
package main
import (
"fmt"
)
type data struct {
name string
}
func main() {
// 使用切片
s := []data{{"Tom"}}
s[0].name = "Jerry" // 切片可以直接修改, 但是 map 不能直接修改
fmt.Println(s) // [{Jerry}]
}