Golang 1.17循环中变量覆盖问题解析
Golang 1.17循环中变量覆盖问题解析 Golang 出现了一些奇怪的行为。我不确定这是 Go 1.17 的问题还是我的代码问题。
基本上,在下面的代码中,我需要保留标题内容为旧值(该值可能与第二个对象的值不同)。我尝试在循环中更新一个对象。更新操作基于第二个对象执行,而该对象在循环中从未被更新。
但是当循环进行第二次迭代时,第二个对象的值却在改变。这不应该发生,因为我没有在任何地方更新第二个对象。
这里不应该更新作为 postedSection 的第二个对象。
package main
import (
"fmt"
)
type Section struct {
PageId int `json:"page_id" bson:"page_id"`
Settings map[string]interface{} `json:"settings" bson:"settings"`
Status bool `json:"status" bson:"status"`
}
func main() {
var languages = []string{"en", "fr"}
postedSection := Section{}
postedSection.PageId = 432
postedSection.Settings = map[string]interface{}{"title": map[string]interface{}{"content": "es posted title"}}
postedSection.Status = true
langWiseSections := make(map[string]Section)
enSec := Section{}
enSec.PageId = 432
enSec.Settings = map[string]interface{}{"title": map[string]interface{}{"content": "en title"}}
enSec.Status = true
langWiseSections["en"] = enSec
esSec := enSec
esSec.Settings = map[string]interface{}{"title": map[string]interface{}{"content": "es title"}}
langWiseSections["es"] = esSec
frSec := enSec
frSec.Settings = map[string]interface{}{"title": map[string]interface{}{"content": "fr title"}}
langWiseSections["fr"] = frSec
for _, language := range languages {
fmt.Println("language-----", language)
section := langWiseSections[language]
fmt.Println("postedSection", postedSection)
section = GetOtherLangSecDataToUpdate(section, postedSection)
fmt.Println("section", section)
}
}
func GetOtherLangSecDataToUpdate(otherLangSec, updatedSection Section) Section {
for fieldName, oldSettIntrfc := range otherLangSec.Settings {
if oldSettIntrfc != nil {
otherLangSec.Settings[fieldName] = updatedSection.Settings[fieldName]
/* continue for all types which are not map[string]interface{} */
switch oldSettIntrfc.(type) {
case map[string]interface{}: // do nothing if map[string]interface{}
default:
continue
}
oldSettings := oldSettIntrfc.(map[string]interface{})
otherLangSecFieldObj := otherLangSec.Settings[fieldName].(map[string]interface{})
switch fieldName {
case "title":
if oldSettings["content"] != nil {
otherLangSecFieldObj["content"] = oldSettings["content"]
}
}
otherLangSec.Settings[fieldName] = otherLangSecFieldObj
}
}
fmt.Println("updatedSection", updatedSection)
return otherLangSec
}
更多关于Golang 1.17循环中变量覆盖问题解析的实战教程也可以访问 https://www.itying.com/category-94-b0.html
这是 Go 语言的默认行为,还是在 Go 1.17 中更新的?
更多关于Golang 1.17循环中变量覆盖问题解析的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
感谢你的帮助。如果你提供的解决方案在我们的系统中不适用,我会在这里联系你。
我认为这样可以做到:https://play.golang.org/p/rLZMKD77sh1
是的,我需要先将更新后的映射复制到旧的映射中,因为可能还有其他字段也需要在所有语言中同步更新,但不包括文本内容。这就是我进行复制的原因。我该如何实现这个需求?
你好,@Amandeep_Kaur,
我认为在第49行,你正在复制嵌套的映射:
otherLangSec.Settings[fieldName] = updatedSection.Settings[fieldName]
因此,当你稍后通过 otherLangSecFieldObj 更改它时,会修改通过 updatedSection 传入的同一个映射。
这种行为并非Go 1.17所特有。映射(map)和切片(slice)是“类引用”的值;它们本质上是指针,因此当你复制它们时,你复制的是指针:
source := map[string]int{"a": 1}
dest := source
dest["a"] = 2
fmt.Println(source["a"]) // 将打印 2
这是一个典型的Go语言中map引用共享导致的问题,不是Go 1.17的bug。问题出现在GetOtherLangSecDataToUpdate函数中对updatedSection.Settings的修改上。
问题分析
当你在循环中调用GetOtherLangSecDataToUpdate(section, postedSection)时,虽然postedSection是按值传递的,但其中的Settings字段是一个map[string]interface{},而map在Go中是引用类型。
关键问题代码:
otherLangSec.Settings[fieldName] = updatedSection.Settings[fieldName]
这行代码实际上将updatedSection.Settings["title"]的引用赋值给了otherLangSec.Settings["title"],导致两个map共享同一个底层数据。
示例重现
简化代码来展示问题:
package main
import "fmt"
type Section struct {
Settings map[string]interface{}
}
func main() {
posted := Section{
Settings: map[string]interface{}{
"title": map[string]interface{}{"content": "posted title"},
},
}
other := Section{
Settings: map[string]interface{}{
"title": map[string]interface{}{"content": "other title"},
},
}
// 修改other的title指向posted的title map
other.Settings["title"] = posted.Settings["title"]
// 现在修改other的title content
titleMap := other.Settings["title"].(map[string]interface{})
titleMap["content"] = "modified content"
// 你会发现posted的title也被修改了!
fmt.Println("posted title:", posted.Settings["title"]) // 输出: map[content:modified content]
}
解决方案
需要深度复制map,而不是共享引用:
func GetOtherLangSecDataToUpdate(otherLangSec, updatedSection Section) Section {
for fieldName, oldSettIntrfc := range otherLangSec.Settings {
if oldSettIntrfc != nil {
// 深度复制map而不是共享引用
switch updatedVal := updatedSection.Settings[fieldName].(type) {
case map[string]interface{}:
// 创建新的map并复制内容
copiedMap := make(map[string]interface{})
for k, v := range updatedVal {
copiedMap[k] = v
}
otherLangSec.Settings[fieldName] = copiedMap
default:
otherLangSec.Settings[fieldName] = updatedSection.Settings[fieldName]
}
switch oldSettIntrfc.(type) {
case map[string]interface{}:
// 处理嵌套逻辑
oldSettings := oldSettIntrfc.(map[string]interface{})
otherLangSecFieldObj := otherLangSec.Settings[fieldName].(map[string]interface{})
switch fieldName {
case "title":
if oldContent, exists := oldSettings["content"]; exists {
otherLangSecFieldObj["content"] = oldContent
}
}
default:
continue
}
}
}
return otherLangSec
}
或者使用更通用的深度复制函数:
func deepCopyMap(original map[string]interface{}) map[string]interface{} {
copied := make(map[string]interface{})
for k, v := range original {
switch val := v.(type) {
case map[string]interface{}:
copied[k] = deepCopyMap(val)
default:
copied[k] = v
}
}
return copied
}
// 在函数中使用
otherLangSec.Settings[fieldName] = deepCopyMap(updatedSection.Settings[fieldName].(map[string]interface{}))
这个问题与Go版本无关,而是map引用语义的常见陷阱。在需要独立副本时,必须显式复制map的内容。

