Why can't I assign to struct in map?

18,260 views
Skip to first unread message

Peter Kleiweg

unread,
Nov 19, 2013, 8:20:38 AM11/19/13
to golan...@googlegroups.com

Jan Mercl

unread,
Nov 19, 2013, 8:30:19 AM11/19/13
to Peter Kleiweg, golang-nuts
On Tue, Nov 19, 2013 at 2:20 PM, Peter Kleiweg <pkle...@xs4all.nl> wrote:
> Why is this invalid?
>
> http://play.golang.org/p/3m3sVjqSVl

LHS at line 16 is not addressable [0].

[0]: http://golang.org/ref/spec#Assignments

-j

Matthias Lieb

unread,
Nov 19, 2013, 8:35:39 AM11/19/13
to golan...@googlegroups.com
I dont know why, but if I make a map with the Type pointer it works.

http://play.golang.org/p/VKhkm4WVHe

Peter Kleiweg

unread,
Nov 19, 2013, 8:54:50 AM11/19/13
to golan...@googlegroups.com, Peter Kleiweg
Op dinsdag 19 november 2013 14:30:19 UTC+1 schreef Jan Mercl:
But, why? 

Bruno Albuquerque

unread,
Nov 19, 2013, 8:59:01 AM11/19/13
to Peter Kleiweg, golang-nuts
Because if the map backing store changes (for example, the map grows
and needs a new backing store to be allocated), the address you might
have to an entry in the map may now be invalid.
> --
> You received this message because you are subscribed to the Google Groups
> "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to golang-nuts...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.



--
http://plus.google.com/+BrunoAlbuquerque

Ian Davis

unread,
Nov 19, 2013, 8:59:35 AM11/19/13
to golan...@googlegroups.com
Addressable is a specific term in the Go language specification meaning whether an address can be accessed for an expression. You can only assign to addressable expressions. Currently map members are not addressable.
 
If you use a pointer instead then you can assign via pointer indirection.

Gustavo Niemeyer

unread,
Nov 19, 2013, 9:08:38 AM11/19/13
to Ian Davis, golan...@googlegroups.com
On Tue, Nov 19, 2013 at 11:59 AM, Ian Davis <m...@iandavis.com> wrote:
> Addressable is a specific term in the Go language specification meaning
> whether an address can be accessed for an expression. You can only assign to
> addressable expressions. Currently map members are not addressable.

He's asking why the respective statement cannot be made to work.

There's no good technical reason, really, other than it's a fairly
minor syntactic improvement that encourages bad code.

This works, and the compiler could easily be changed to do the same internally:

v := m[k]
v.A += 1
m[k] = v

In fact, it already does something quite similar if you do:

m[k] += 1


gustavo @ http://niemeyer.net

Ian Davis

unread,
Nov 19, 2013, 9:16:44 AM11/19/13
to golan...@googlegroups.com

On Tue, Nov 19, 2013, at 02:08 PM, Gustavo Niemeyer wrote:
> On Tue, Nov 19, 2013 at 11:59 AM, Ian Davis <m...@iandavis.com> wrote:
> > Addressable is a specific term in the Go language specification meaning
> > whether an address can be accessed for an expression. You can only assign to
> > addressable expressions. Currently map members are not addressable.
>
> He's asking why the respective statement cannot be made to work.
>

The OP's question "But, why?" was in response to Jan noting that the
expression was not addressable. I took that to mean the OP wanted to
understand more.

Ian

Jan Mercl

unread,
Nov 19, 2013, 9:17:30 AM11/19/13
to Gustavo Niemeyer, Ian Davis, golang-nuts
On Tue, Nov 19, 2013 at 3:08 PM, Gustavo Niemeyer <gus...@niemeyer.net> wrote:
> There's no good technical reason, really, other than it's a fairly
> minor syntactic improvement that encourages bad code.
>
> This works, and the compiler could easily be changed to do the same internally:
>
> v := m[k]
> v.A += 1
> m[k] = v
>
> In fact, it already does something quite similar if you do:
>
> m[k] += 1

The last case is well specified. 'a += b' is 'a = a + b'. But the
former example is different. 'm[k] = v' is a syntax (sugar) for some
RTL 'setMap(k, v)'. Now 'm[k].A = v' becomes (like) 'setMap(k.A, v)'
which obviously cannot work as desired. Returning to the += case,
'm[k] = m[k]+1' -> 'setMap(k, getMap(k)+1)', which is clear to work as
intended.

-j

Gustavo Niemeyer

unread,
Nov 19, 2013, 9:27:28 AM11/19/13
to golang-nuts
m[k] += 1 and m[k].A += 1 are both set(m[k], change(m[k])) and can
both be made to work fine if that was desired. The unsupported variant
encourages bad code, and that's my guess as to why it's unsupported. I
don't have insight into the actual design decision, though.
--

gustavo @ http://niemeyer.net

RickyS

unread,
Nov 19, 2013, 4:03:56 PM11/19/13
to golan...@googlegroups.com
1.  The inconsistency may be a language bug (design or implementation) or the result of some deep knowledge that eludes me.
      (Or it could be some shallow knowledge that eludes me.)

BUT:

2.  Noodling around with this, I stumbled onto AUTO-VIVIFICATION of map elements on line 28 of http://play.golang.org/p/ogWHcaFYb9
     I don't remember this being in the spec.  All I can find in the spec is:
Elements may be added during execution using assignments and retrieved with index expressions;

    What I stumbled on is that retrieval of a non-existent map element creates the element.

Matthew Kane

unread,
Nov 19, 2013, 4:15:56 PM11/19/13
to RickyS, golang-nuts
It doesn't create the element. Retrieving a non-existent map element
yields the zero value for the map's value type.

"if the map is nil or does not contain such an entry, a[x] is the zero
value for the value type of M"

The map is unchanged. To see if the zero value is actually contained
in the map, use the special form of the index expression

var v, ok = a[x]

If ok is false, then the key did not exist in the map.
> --
> You received this message because you are subscribed to the Google Groups
> "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to golang-nuts...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.



--
matt kane
twitter: the_real_mkb / nynexrepublic
http://hydrogenproject.com

Iñaki Baz Castillo

unread,
Nov 19, 2013, 4:45:41 PM11/19/13
to Gustavo Niemeyer, Ian Davis, golang-nuts
2013/11/19 Gustavo Niemeyer <gus...@niemeyer.net>:
> This works, and the compiler could easily be changed to do the same internally:
>
> v := m[k]
> v.A += 1
> m[k] = v

Is not the same by removing the last line?:

v := m[k]
v.A += 1

Or does it not work because the first line is copying the entire value
in m[k] so v becomes a copy but not the original?

How efficient is having to do all the above for accessing a struct
field within a map entry? (this is: copy value, modify new value,
reassing new value to the original value).


--
Iñaki Baz Castillo
<i...@aliax.net>

Dave Cheney

unread,
Nov 19, 2013, 4:51:12 PM11/19/13
to Iñaki Baz Castillo, Gustavo Niemeyer, Ian Davis, golang-nuts


> On 20 Nov 2013, at 8:45, Iñaki Baz Castillo <i...@aliax.net> wrote:
>
> 2013/11/19 Gustavo Niemeyer <gus...@niemeyer.net>:
>> This works, and the compiler could easily be changed to do the same internally:
>>
>> v := m[k]
>> v.A += 1
>> m[k] = v
>
> Is not the same by removing the last line?:
>
> v := m[k]
> v.A += 1
>
> Or does it not work because the first line is copying the entire value
> in m[k] so v becomes a copy but not the original?

Everything in Go returns a copy, maps are no different. v is a copy of the contents of the value at m[k].

>
> How efficient is having to do all the above for accessing a struct
> field within a map entry? (this is: copy value, modify new value,
> reassing new value to the original value).

Benchmark it, don't guess.

>
>
> --
> Iñaki Baz Castillo
> <i...@aliax.net>
>

Nico

unread,
Nov 20, 2013, 6:25:36 AM11/20/13
to golan...@googlegroups.com
On 19/11/13 21:51, Dave Cheney wrote:
> Everything in Go returns a copy, maps are no different. v is a copy of the contents of the value at m[k].

This fact has come up several times in the mailing list, but it's not in
the specs. Should it be mentioned there?


C Banning

unread,
Nov 20, 2013, 9:37:29 AM11/20/13
to golan...@googlegroups.com

roger peppe

unread,
Nov 20, 2013, 10:17:20 AM11/20/13
to C Banning, golang-nuts
On 20 November 2013 14:37, C Banning <clba...@gmail.com> wrote:
> http://play.golang.org/p/iLRx1chQEH

Going a little bit further than that:
http://play.golang.org/p/26l0hez1pZ

Jesse McNelis

unread,
Nov 20, 2013, 6:07:31 PM11/20/13
to Nico, golang-nuts
"if the map contains an entry with key x, a[x] is the map value with key x and the type of a[x] is the value type of M"

The use of the word 'copy' in Dave's post is just because a lot of programmers have forgotten what a 'value' is and keep mentally auto-dereferencing their variables.



 
 -- 
=====================
http://jessta.id.au

Nico

unread,
Nov 21, 2013, 4:53:22 AM11/21/13
to Jesse McNelis, golang-nuts
I think Dave's comment is not restricted to maps. It's a general feature
of Go assignments:

http://play.golang.org/p/ug5_LjjjPM

package main

import "fmt"

type T struct {
Val int
}

func main() {
var a, b T
a.Val = 1
b = a
b.Val = 2
fmt.Println(a, b)
}

Jesse McNelis

unread,
Nov 21, 2013, 5:49:08 AM11/21/13
to Nico, golang-nuts


On 21/11/2013 8:53 PM, "Nico" <nicolas...@gmail.com> wrote:
>
> I think Dave's comment is not restricted to maps. It's a general feature of Go assignments:
>
> http://play.golang.org/p/ug5_LjjjPM
>

It's also a general feature of assignment in almost every programming language in common use.

Nico

unread,
Nov 21, 2013, 5:58:03 AM11/21/13
to Jesse McNelis, golang-nuts
On 21/11/13 10:49, Jesse McNelis wrote:
>
> On 21/11/2013 8:53 PM, "Nico" <nicolas...@gmail.com
Isn't Java:

class T {
int Val;
}

public class Main {
public static void main(String[] args) {
T a = new T();
T b = new T();
a.Val = 1;
b = a;
b.Val = 2;
System.out.println("a = " + a.Val + "\nb = " + b.Val);
}
}

a counterexample.


Jesse McNelis

unread,
Nov 21, 2013, 6:30:24 AM11/21/13
to Nico, golang-nuts


On 21/11/2013 9:58 PM, "Nico" <nicolas...@gmail.com> wrote:
>
> Isn't Java:
>

This is what I mean by 'mentally auto dereferencing variables'.
You're confusing the reference to the value with the value it refers to.
Check the internet for 'is java pass by value or pass by reference?' for the endless discussions of other programmer's with the same confusion.

Nico

unread,
Nov 21, 2013, 7:19:53 AM11/21/13
to Jesse McNelis, golang-nuts
I'm aware of the discussions. People don't seem to agree what the
meaning of pass by value/reference is.

Anyway, I've noticed the FAQ:

http://golang.org/doc/faq#pass_by_value

already discusses the point that Dave made.

Unfortunately it is a question about function arguments, which won't be
easily found by someone puzzled by the behaviour of struct assignments
or map ranges.

Iñaki Baz Castillo

unread,
Nov 21, 2013, 7:47:46 AM11/21/13
to Nico, Jesse McNelis, golang-nuts
2013/11/21 Nico <nicolas...@gmail.com>:
> I think Dave's comment is not restricted to maps. It's a general feature of
> Go assignments:
>
> http://play.golang.org/p/ug5_LjjjPM

IMHO this could be interesting for beginners (as me):

http://play.golang.org/p/JdxrcTiS-w

It would have been easier for me to find something like that when I
started learning Go.

Nico

unread,
Nov 21, 2013, 8:19:08 AM11/21/13
to Iñaki Baz Castillo, Jesse McNelis, golang-nuts
On 21/11/13 12:47, Iñaki Baz Castillo wrote:
> 2013/11/21 Nico <nicolas...@gmail.com>:
>> I think Dave's comment is not restricted to maps. It's a general feature of
>> Go assignments:
>>
>> http://play.golang.org/p/ug5_LjjjPM
>
> IMHO this could be interesting for beginners (as me):
>
> http://play.golang.org/p/JdxrcTiS-w
>
> It would have been easier for me to find something like that when I
> started learning Go.
>

http://golang.org/doc/faq#methods_on_values_or_pointers

Iñaki Baz Castillo

unread,
Nov 21, 2013, 8:24:20 AM11/21/13
to Nico, Jesse McNelis, golang-nuts
2013/11/21 Nico <nicolas...@gmail.com>:
>> IMHO this could be interesting for beginners (as me):
>>
>> http://play.golang.org/p/JdxrcTiS-w
>>
>> It would have been easier for me to find something like that when I
>> started learning Go.
>>
>
> http://golang.org/doc/faq#methods_on_values_or_pointers

Yes:

"First, and most important, does the method need to modify the
receiver? If it does, the receiver must be a pointer. (Slices and maps
act as references, so their story is a little more subtle, but for
instance to change the length of a slice in a method the receiver must
still be a pointer.) In the examples above, if pointerMethod modifies
the fields of s, the caller will see those changes, but valueMethod is
called with a copy of the caller's argument (that's the definition of
passing a value), so changes it makes will be invisible to the
caller."

However I consider that a single code example (may be like the one I
provided) would it make much easier to understand (as Go here behaves
much different than other languages with map/hash/dictionary native
support).

pe...@126.com

unread,
Nov 21, 2013, 10:17:05 AM11/21/13
to golan...@googlegroups.com
func main() {

items := make(map[string]Type)

items["q"] = Type{}    
items["q"].A = "abc"   // items["q"] is not the same object as previous line Type{} , even if it works. the original object Type{} in previous line is not changed.

fmt.Println(items)
}

if we change map to map[string][]int, it works because slice is pointer type , and struct is not.

package main

import (
"fmt"
)

func main() {
m1 := map[string][]int{"5": make([]int, 3, 10)}
fmt.Println(m1)
m1["5"][1] = 2
fmt.Println(m1)
}




在 2013年11月19日星期二UTC+8下午9时20分38秒,Peter Kleiweg写道:

pe...@126.com

unread,
Nov 21, 2013, 10:26:17 AM11/21/13
to golan...@googlegroups.com
if you change struct to slice ,it works because struct is not a pointer type.


package main

import (
"fmt"
)

func main() {
m1 := map[string][]int{"5": []int{1, 2, 3}}
Message has been deleted

Chris Hines

unread,
Nov 22, 2013, 11:36:24 AM11/22/13
to golan...@googlegroups.com, Jesse McNelis
Only if you forget that "T a = new T()" declares a *reference* named "a" to a new object of type T.

In Go the equivalent is "a := new(T)" or "a:= &T{}" which are both distinct from "var a T". The first two create *pointers* named "a" to a new instance of type T. The last one creates a new instance of type T named "a".

When you have a pointer to something you can overwrite the pointer to point at something else and you can dereference the pointer to change the thing pointed at. When you just have a value you can only change the value; the name you give the value cannot be made to name a value at a different location in memory.

You can only create values for primitives in Java. So a corresponding example in Java might be:

public class Main { 
   public static void main(String[] args) { 
     int a = 1; 
     int b = a; 
     b = 2; 
     System.out.println("a = " + a + "\nb = " + b); 
   } 
}

Nico

unread,
Nov 22, 2013, 11:55:48 AM11/22/13
to golan...@googlegroups.com
I think this is the reason why some find maps and ranges confusing.

Maps usually behave like "reference" types, see:


http://play.golang.org/p/VWTDkLO7iR

package main

import "fmt"

func main() {
a := make(map[int]string)
a[1] = "one"
b := a
b[1] = "jeden"
fmt.Println(a, b)
}


but in a range loop:


for k, v := range a {
...
}


often it is missed that v is a copy of the value contained in a, and not
a itself.

Chris Hines

unread,
Nov 22, 2013, 2:03:07 PM11/22/13
to golan...@googlegroups.com
I agree. I have been tripped up by that a few times myself. It is easy to forget that the map is a reference to the map contents, but it does not hold references (unless it explicitly stores pointers). The same can be said of slices. Unfortunately indexing into a slice is addressable and indexing into a map is not. The syntax looks the same, but the allowed operations are not the same.

For example, the code from the original question that uses a map:


Works when changed to use a slice:


And also works when the map stores pointers instead of values:


Gustavo Niemeyer

unread,
Nov 28, 2013, 1:45:19 PM11/28/13
to golang-nuts
On Tue, Nov 19, 2013 at 12:27 PM, Gustavo Niemeyer <gus...@niemeyer.net> wrote:
> m[k] += 1 and m[k].A += 1 are both set(m[k], change(m[k])) and can
> both be made to work fine if that was desired. The unsupported variant
> encourages bad code, and that's my guess as to why it's unsupported. I
> don't have insight into the actual design decision, though.

Turns out it's being considered for 1.3, actually:

http://golang.org/issue/3117


gustavo @ http://niemeyer.net

andyxning

unread,
Mar 9, 2016, 5:59:03 AM3/9/16
to golang-nuts
This makes more confusing for me because i must remember this difference and those two can not make me remember them in theory. The theory runs for map however when we come to slice it breaks. :-(

在 2013年11月23日星期六 UTC+8上午3:03:07,Chris Hines写道:
Reply all
Reply to author
Forward
0 new messages