解决 Go 中结构体成员不能修改的问题的两种方法
· 阅读需 3 分钟
map 引用类型的问题
在 Go 中,如果结构体的成员包含引用类型(如切片、map 或指针),当将该结构体作为 map 的值时,需要注意一些细节。具体来说,如果结构体的成员是引用类型,那么在获取结构体值并修改其引用类型成员时,会遇到不能修改的问题。
package main
import "fmt"
type Person struct {
name string
age int
}
func main() {
m := make(map[string]Person)
// 添加键值对
m["john"] = Person{"John", 30}
// 获取 john 对应的 Person 结构体值
john := m["john"]
// 修改 john 的 age 字段
john.age = 40
// 此时输出 map 中 "john" 对应的 age 字段值
fmt.Println(m["john"].age) // 输出 30,而不是 40
}
我们创建了一个 Person
结构体类型,并将其作为值存储在了一个 map
中。然后,我们从 map
中取出了键为 "john" 的 Person
结构体值,并尝试修改其 age
字段的值。但是,即使我们修改了 john
的 age
字段,最终打印出来的结果仍然是原来 map
中的值,即 30,而不是修改后的 40。这是因为在 Go 中,m["john"]
返回的是 Person
结构体的值的副本,而不是原始值的引用。因此,修改 john
并不会影响原始值在 map
中的值
如何修改呢?
第一种方式
可以通过使用指针类型来存储结构体值,或者在获取结构体值时使用指针,并且修改成员时也要通过指针来操作。
package main
import "fmt"
type Person struct {
name string
age int
}
func main() {
m := make(map[string]*Person)
// 添加键值对,值为 Person 结构体的指针
m["john"] = &Person{"John", 30}
// 获取 john 对应的 Person 结构体指针
john := m["john"]
// 修改 john 指向的结构体的 age 字段
john.age = 40
// 此时输出 map 中 "john" 对应的 age 字段值
fmt.Println(m["john"].age) // 输出 40
}
我们将 map
中的值类型指定为 *Person
,即 Person
结构体的指针。当我们获取键为 "john" 的值时,会得到 Person
结构体的指针,然后可以通过指针来修改结构体的字段。
第二种
package main
import "fmt"
type Person struct {
name string
age int
}
func main() {
m := make(map[string]Person)
// 添加键值对
m["john"] = Person{"John", 30}
// 获取 john 对应的 Person 结构体值的指针
john := &m["john"]
// 修改 john 指向的结构体的 age 字段
john.age = 40
// 此时输出 map 中 "john" 对应的 age 字段值
fmt.Println(m["john"].age) // 输出 40
}
先获取了键为 "john" 的 Person
结构体值的指针,并将其赋给 john
变量。然后,通过指针来修改 john
指向的结构体的字段。
使用建议
建议使用第一种方式,使用指针可以避免复制结构体值,提高效率,并且可以确保所有修改都是针对同一个结构体值的引用,避免了值的复制和不一致的状态。另外,使用指针还可以避免因为值复制而导致的内存占用过多的问题,尤其是在处理大型结构体时更为明显。