Может ли кто-нибудь объяснить, почему значение моей структуры «Имя» изменяется после того, как оно было добавлено к новому фрагменту?
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
Вы напрямую изменяете указатель на одну структуру в s
, а затем дважды сохраняете ссылку на это значение.
Создайте новый, измененный экземпляр для добавления
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)
}
}
}
Более простая демонстрация того же эффекта:
m1 := map[string]int{"key": 1}
m2 := m1
m2["key"] = 2
fmt.Println(m1) // prints "map[key:2]"
Причина в том, что когда вы копируете карту, вы на самом деле просто копируете указатель на ту же карту, а не создаете отдельную карту.
В вашем случае вы копируете структуру, содержащую карту. При копировании структуры копируются все ее поля, что, по сути, и нужно, за исключением проблемы, заключающейся в том, что копирование поля карты не создает отдельную карту.
Чтобы скопировать карту, вы можете использовать maps.Clone
[ссылка на документ]:
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"
. Поскольку карта является ссылочным типом, вы изменяете значение в checkedStaff
, которое теперь равно [{Name:map[ja:Tom] Function:Cashier}]
, и добавляете его к checkedStaff
, поэтому строка продолжается с [{Name:map[ja:Tom] Function:Cashier} {Name:map[ja:Tom] Function:Cashier}]
.
Обычно вы не хотите изменять исходную структуру, над которой работаете. Если вы что-то трансформируете (путем разделения или обрезки), не возвращайте это в исходную структуру, а создайте новую.
Это не совсем правильно. В Go копирование структуры не копирует неявный указатель на ту же базовую структуру; скорее, он фактически выполняет копирование по полям. Проблемой является только карта, поскольку копирование карты копирует неявный указатель на ту же самую базовую карту.