Variables are overriding in loop in go 1.17

There is some weird behavior of Golang. I am not sure it is in Go1.17 or in my code.

Basically In the below code I need to preserve the title content with the old value (which can be different from the second object value). I am trying to update an object in a loop. Updation is performed on the basis of a second object which is never updated in the loop.

But when loop’s second pass is running the second objects value is changing. It should not because I am not updating the second object anywhere.

The second object which is posted section should not be updated here.

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
}

Hi, @Amandeep_Kaur,

I think on line 49, you’re copying the nested map:

otherLangSec.Settings[fieldName] = updatedSection.Settings[fieldName]

So when you change it later via otherLangSecFieldObj, that mutates the same map that was passed in via updatedSection.

Yes I need to copy the updated map into old one first because there can be some other fields also that I need to reflect in all languages but not the texts. That is why copied. How can I achieve this requirement ?

I think this will do it: https://play.golang.org/p/rLZMKD77sh1

Is this Golang default behavior or it is updated in go 1.17 ?

The behavior is not specific to Go 1.17. Maps and slices are “reference-like” values; they’re essentially pointers, so when you copy them, you’re copying the pointer:

source := map[string]int{"a": 1}

dest := source
dest["a"] = 2

fmt.Println(source["a"]) // will print 2
1 Like

Thanks man for your help. I will reach out to you here if the solution you provided did not work with our system.