Golang 值类型 指针类型
package main
import "fmt"
type Pomodoro struct {
todos []string
}
type Alice struct {
pomodoro map[uint16]Pomodoro
}
type Alex struct {
pomodoro map[uint16]*Pomodoro
}
func main() {
// 指针map演示
alex := Alex{pomodoro: make(map[uint16]*Pomodoro)}
alex.pomodoro[1] = &Pomodoro{todos: []string{"初始任务"}}
// 修改指针指向的内容
alex.pomodoro[1].todos = append(alex.pomodoro[1].todos, "新增任务")
fmt.Println(alex.pomodoro[1].todos) // [初始任务 新增任务]
// 值map演示
alice := Alice{pomodoro: make(map[uint16]Pomodoro)}
alice.pomodoro[1] = Pomodoro{todos: []string{"初始任务"}}
// 直接修改副本无效
tmp := alice.pomodoro[1]
// alice.pomodoro[1].todos = append(alice.pomodoro[1].todos, "无效新增") // error=> cannot assign to struct field alice.pomodoro[1].todos in mapcompilerUnaddressableFieldAssign
tmp.todos = append(tmp.todos, "无效新增")
fmt.Println(alice.pomodoro[1].todos) // [初始任务] (未改变)
// 正确修改方式
tmp = alice.pomodoro[1]
tmp.todos = append(tmp.todos, "有效新增")
alice.pomodoro[1] = tmp
fmt.Println(alice.pomodoro[1].todos) // [初始任务 有效新增]
}
你的代码很好地展示了在Go语言中使用指针和值类型作为map的值时的区别。通过对比Alex和Alice两个结构体,我们可以清楚地看到直接操作值类型映射与操作指向值类型的指针映射的不同之处。
代码分析
-
对于
Alex结构体:- 使用了指针类型的
map,即map[uint16]*Pomodoro。 - 这意味着你可以直接修改
Pomodoro实例的内容,因为你是通过指针访问它们的。因此,在你向todos列表添加新任务时,原始数据被修改了。
- 使用了指针类型的
-
对于
Alice结构体:- 使用的是值类型的
map,即map[uint16]Pomodoro。 - 当你尝试直接修改从映射中获取的
Pomodoro实例的字段时(如注释所示),这是不允许的,因为你不能直接修改从映射中检索出来的结构体副本。 - 正确的做法是先将副本修改完毕后再将其赋值回映射中,以更新原始数据。
- 使用的是值类型的
关键点总结
-
使用指针:当你希望对映射中的元素进行修改而不必每次都重新赋值回映射时,使用指针是一个好选择。这使得可以直接修改对象,避免不必要的复制开销。
-
使用值类型:如果你不打算修改映射中的元素,或者元素非常小,使用值类型可能是更简单的方式。但是,如果需要修改,则需要先修改副本再将整个副本赋值回映射中。
浙公网安备 33010602011771号