Cannot assign to struct field in a map

Go

Go Problem Overview


I have the data structure like this:

type Snapshot struct {
  Key   string
  Users []Users
}

snapshots := make(map[string] Snapshot, 1)

// then did the initialization
snapshots["test"] = Snapshot {
  Key: "testVal",
  Users: make([]Users, 0),
}

Users is another struct.

Then when I tried to append some new Users values in the Users slice like this:

snapshots["test"].Users = append(snapshots["test"].Users, user)

I kept getting this error:

cannot assign to struct field snapshots["test"].Users in map

Also tried the workaround here https://github.com/golang/go/issues/3117 so like this:

tmp := snapshots["test"].Users
tmp = append(tmp, user)
snapshots["test"].Users = tmp

But no luck, still exactly same error.

And also tried to declare the map with pointer, so: snapshots := make(map[string] *Snapshot, 1), still no luck.

Go Solutions


Solution 1 - Go

For those looking for a simpler example:

This is wrong:

type myStruct struct{
   Field int
}

func main() {
   myMap := map[string]myStruct{
		"key":{
    		Field: 1,
		},
   }

   myMap["key"].Field = 5
}

Because myMap["key"] is not "addressable".

This is correct:

type myStruct struct{
   Field int
}

func main(){
   myMap := map[string]myStruct{
	   "key":{
		   Field: 1,
	   },
   }

   // First we get a "copy" of the entry
   if entry, ok := myMap["key"]; ok {

	   // Then we modify the copy
	   entry.Field = 5
	
	   // Then we reassign map entry
	   myMap["key"] = entry
   }

   // Now "key".Field is 5
   fmt.Println(myMap) // Prints map[key:{5}]
}

Here you have a working example.

Solution 2 - Go

First, for this question, the solution in this post https://stackoverflow.com/questions/32751537/why-do-i-get-a-cannot-assign-error-when-setting-value-to-a-struct-as-a-value-i works perfectly fine.

Then, finally figured out why after I already changed to use pointer my case still doesn't work, refer to the below very simple code:

a := make([]int, 3)
fmt.Println(len(a))

b := make(map[string]string, 3)
fmt.Println(len(b))

What do think the output will be? I simply thought it is all would be: 3, but actually for the map, the output will be 0

Then later in the map initialization process, i used a for loop and with this value len(snapshots), that means the initialization process will never get run...

Yea, that is the reason.

Solution 3 - Go

For my use case, I needed to update the entries fairly often. Thus, modifying the copy and reassigning it to the map entry would have been very inefficient. An alternative way could be to use a pointer to a struct instead. (I know it won't fit for all use cases, but just in case yours is flexible enough to use either a struct or a pointer to it...)

type bigStruct struct {
    val1 int
    val2 bool
    val3 string
}

newMap := make(map[string]*bigStruct)

newMap["struct1"] = &bigStruct{1, true, "example1"}

// and can now modify the entries normally
newMap["struct1"].val1 = 2
newMap["struct1"].val2 = false
newMap["struct1"].val3 = "example2"

See the full code here.

Solution 4 - Go

What I ended up doing to use my struct map in a loop was the following:

type testStruct struct {
  a string
  b int
}

func main() {
  mapTest := make(map[string]testStruct)
  abc := [3]string{"a", "b", "c"}

  for i := 0; i < len(abc); i++ {
    var temp testStruct
    temp.a = abc[i]
    temp.b = i
    mapTest[abc[i]] = temp
  }

  fmt.Println(mapTest)
}

Output should be:

map[b:{b 1} c:{c 2} a:{a 0}]

It's not appending, but should work to assign multiple values to a struct map, alternatively you could do the following and allow the map to reference its own values:

func main() {
  mapTest := make(map[string]testStruct)

  abc := [3]string{"a", "b", "c"}
  for i := 0; i < len(abc)*2; i++ {
    temp := mapTest[abc[i%3]]
    temp.a = abc[i%3]
    temp.b = temp.b + i
    mapTest[abc[i%3]] = temp
  }

  fmt.Println(mapTest)
}

Which should output:

map[a:{a 3} b:{b 5} c:{c 7}]

Note that no errors are raised when we reference an empty struct value, this is because when we initialize our struct, its values start out as empty values but not nil (0 for int, "" for string, etc.)

Solution 5 - Go

The reason that it is not possible to do what the Asker is trying is due to addressability.

The exact error you are receiving as per the errors in the spec is _UnaddressableFieldAssign which is raised when you try to assign to an unaddressable value.

> _UnaddressableFieldAssign occurs when trying to assign to a struct field in a map value. Example:

   func f() {
	  m := make(map[string]struct{i int})
	  m["foo"].i = 42
	}

Addressablility is defined in the spec as

> The operand must be addressable, that is, either a variable, pointer indirection, or slice indexing operation; or a field selector of an addressable struct operand; or an array indexing operation of an addressable array. As an exception to the addressability requirement, x may also be a (possibly parenthesized) composite literal. If the evaluation of x would cause a run-time panic, then the evaluation of &x does too.

Values in a map are not addressable, but using pointers for indirection allow those values to be addressable

Here is a great explanation that really helped me understand the addressable vs unaddressable concept in golang

> There are a number of important things that are not addressable. For > example, values in a map and the return values from function and > method calls are not addressable. The following are all errors: > &m["key"]
> &afunc() > > &t.method()

How this relates to the initial question asked, is that everything on the left-hand-side is unaddressable as per the spec - and hence is an invalid use of the assignment operator

> Each left-hand side operand must be addressable, a map index expression, or (for = assignments only) the blank identifier.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionlnshiView Question on Stackoverflow
Solution 1 - GoJairo LozanoView Answer on Stackoverflow
Solution 2 - GolnshiView Answer on Stackoverflow
Solution 3 - GoAnkit KumarView Answer on Stackoverflow
Solution 4 - GoDevyzrView Answer on Stackoverflow
Solution 5 - GoTjad ClarkView Answer on Stackoverflow