有人可以解释为什么我的结构体的值“Name”在附加到新切片后会发生变化吗?
package main
import (
"fmt"
"strings"
"golang.org/x/text/language"
)
func main() {
type Staff struct {
Name map[language.Tag]string
Function string
}
var s = []Staff{
Staff{
Name: map[language.Tag]string{language.Japanese: "Mike/Tom"},
Function: "Cashier",
},
}
checkedStaff := []Staff{}
// check for multiple staff in one role
for _, staff := range s {
if !strings.Contains(staff.Name[language.Japanese], "/") {
checkedStaff = append(checkedStaff, staff)
continue
}
split := strings.Split(staff.Name[language.Japanese], "/")
for _, staffNameNative := range split {
staff.Name[language.Japanese] = strings.TrimSpace(staffNameNative)
checkedStaff = append(checkedStaff, staff)
fmt.Printf("%+v", checkedStaff)
}
}
}
我希望切片
checkedStaff
包含这些值:[{Name:map[ja:Mike] Function:Cashier} {Name:map[ja:Tom] Function:Cashier}]
但它包含
[{Name:map[ja:Tom] Function:Cashier} {Name:map[ja:Tom] Function:Cashier}]
因此追加后,切片
checkedStaff
中的名称被修改。
参见Go Playground上的代码: https://go.dev/play/p/ovKO4Hc7N3t
相同效果的更简单演示:
m1 := map[string]int{"key": 0}
m2 := m1
m2["key"] = 1
fmt.Println(m1) // prints "map[key:1]"
[游乐场链接]
原因是,当您复制地图时,您实际上只是复制指向同一地图的指针,而不是创建单独的地图。
在您的例子中,您正在复制一个包含地图的结构。复制结构体会复制其每个字段,这基本上就是您想要的,除了复制映射字段不会创建单独的映射的问题。
要复制地图,您可以使用
maps.Clone
[doc link]:
copyOfStaff := staff
copyOfStaff.Name = maps.Clone(staff.Name)
copyOfStaff.Name[language.Japanese] = strings.TrimSpace(staffNameNative)
checkedStaff = append(checkedStaff, copyOfStaff)
[游乐场链接]
好吧,让我们稍微简化一下你的程序(Go Playground):
package main
import (
"fmt"
"golang.org/x/text/language"
)
func main() {
type Staff struct {
Name map[language.Tag]string
Function string
}
staff := Staff{
Name: map[language.Tag]string{},
Function: "Cashier",
}
checkedStaff := []Staff{}
split := []string{"Mike", "Tom"}
for _, staffNameNative := range split {
staff.Name[language.Japanese] = staffNameNative
fmt.Printf(">> checkedStaff + staff: %+v + %+v\n", checkedStaff, staff)
checkedStaff = append(checkedStaff, staff)
fmt.Printf("checkedStaff: %+v\n", checkedStaff)
}
}
那么,会发生什么?
在第一次执行中,您设置了
staff.Name[language.Japanese] = "Mike"
,因此 staff = {Name:map[ja:Mike] Function:Cashier}
,将其附加到 []
,从而产生以 [{Name:map[ja:Mike] Function:Cashier}]
开头的行。
现在第二个循环执行了
staff.Name[language.Japanese] = "Tom"
。由于 map 是引用类型,因此您修改了 checkedStaff
中的值,现在是 [{Name:map[ja:Tom] Function:Cashier}]
,并将其附加到 checkedStaff
,因此该行以 [{Name:map[ja:Tom] Function:Cashier} {Name:map[ja:Tom] Function:Cashier}]
继续。
通常您不想修改正在处理的源结构。如果您改变某些东西(通过分割或修剪),不要将其放回原始结构,而是创建一个新结构。
您直接修改指向
s
中的一个结构体的指针,然后将 reference 存储到该值两次。
创建一个新的、修改后的实例来追加
package main
import (
"fmt"
"strings"
"golang.org/x/text/language"
)
func main() {
type Staff struct {
Name map[language.Tag]string
Function string
}
var s = []Staff{
Staff{
Name: map[language.Tag]string{language.Japanese: "Mike/Tom"},
Function: "Cashier",
},
}
checkedStaff := []Staff{}
// check for multiple staff in one role
for _, staff := range s {
if !strings.Contains(staff.Name[language.Japanese], "/") {
c := Staff{
Name: staff.Name,
Function: staff.Function,
}
checkedStaff = append(checkedStaff, c)
continue
}
split := strings.Split(staff.Name[language.Japanese], "/")
for _, staffNameNative := range split {
c := Staff{
Name: map[language.Tag]string{language.Japanese: strings.TrimSpace(staffNameNative)},
Function: staff.Function,
}
checkedStaff = append(checkedStaff, c)
fmt.Printf("%+v\n", checkedStaff)
}
}
}