map[string]interface{} edit

2,062 views
Skip to first unread message

Tobias Matt

unread,
Oct 14, 2010, 1:53:05 PM10/14/10
to golang-nuts
Hi.

I tried to make a little programm like this:

1 package main
2
3 import (
4 "fmt"
5 )
6
7 type line struct {
8 number int
9 content string
10 }
11
12 func main() {
13 m := map[string]interface{}{}
14 m["Line1"] = line{1, ""}
15 m["Line2"] = line{2, ""}
16 m["Line3"] = line{3, ""}
17 m["Line3"].content = "something"
18 fmt.Printf("%v\n", m)
19 }

but the line 17 doesn't work. I don't know how I can edit the content
in the struct.

jimmy frasche

unread,
Oct 14, 2010, 2:04:03 PM10/14/10
to Tobias Matt, golang-nuts
On Thu, Oct 14, 2010 at 10:53 AM, Tobias Matt <t.ma...@gmail.com> wrote:
> 17 m["Line3"].content = "something"

m["Line3"].(line).content = "something"

You need to tell Go that the interface{} value is that type so it can
unbox the value.

Ian Lance Taylor

unread,
Oct 14, 2010, 2:04:55 PM10/14/10
to Tobias Matt, golang-nuts
Tobias Matt <t.ma...@gmail.com> writes:

Well, you have a map to empty interface. So m["line3"] is going to give
you an empty interface. You can't refer to a field in an empty
interface.

There is another issue, though, which is a rather tricky part of the
language. You can assign to a map index expression, but you can't
assign to a part of it. This is because you can't take the address of a
map index, so a map index is not addressable, and so you can not assign
to a field of it. This is a logical consequence of the rules, but the
end result is odd.

Anyhow, what you have to do here, assuming you want to keep the map to
an empty interface, is something like

l := m["Line3"].(line)
l.content = "something"
m["Line3"] = l

Ian

Michael Hoisie

unread,
Oct 14, 2010, 2:07:59 PM10/14/10
to golang-nuts
func main() {
m := map[string]interface{}{}
m["Line1"] = &line{1, ""}
m["Line2"] = &line{2, ""}
m["Line3"] = &line{3, ""}
m["Line3"].(*line).content = "hello"
fmt.Printf("%v\n", m)
}

Make sure you store pointers, not values.

It's a little weird, though, that if you just store values, you get a
compile error when you try to assign to the value:
m["Line3"].(*line).content = "hello"
map.go:17: cannot assign to (m["Line3"]).(line).content
Is that a bug?

- Mike

Tobias Matt

unread,
Oct 14, 2010, 2:24:55 PM10/14/10
to golang-nuts
> Well, you have a map to empty interface.  So m["line3"] is going to give
> you an empty interface.  You can't refer to a field in an empty
> interface.
>
> There is another issue, though, which is a rather tricky part of the
> language.  You can assign to a map index expression, but you can't
> assign to a part of it.  This is because you can't take the address of a
> map index, so a map index is not addressable, and so you can not assign
> to a field of it.  This is a logical consequence of the rules, but the
> end result is odd.
>
> Anyhow, what you have to do here, assuming you want to keep the map to
> an empty interface, is something like
>
>         l := m["Line3"].(line)
>         l.content = "something"
>         m["Line3"] = l
>
> Ian

Ok, thanks. It work, but is a little bit ugly :)

Tobias Matt

unread,
Oct 14, 2010, 2:37:14 PM10/14/10
to golang-nuts
On 14 Okt., 20:07, Michael Hoisie <hoi...@gmail.com> wrote:
> func main() {
>         m := map[string]interface{}{}
>         m["Line1"] = &line{1, ""}
>         m["Line2"] = &line{2, ""}
>         m["Line3"] = &line{3, ""}
>         m["Line3"].(*line).content = "hello"
>         fmt.Printf("%v\n", m)
>
> }
>
> Make sure you store pointers, not values.

It work very well. I will try it this way.

> It's a little weird, though, that if you just store values, you get a
> compile error when you try to assign to the value:
> m["Line3"].(*line).content = "hello"
> map.go:17: cannot assign to (m["Line3"]).(line).content
> Is that a bug?

I don't know. My knowledge about the go language is to low to assess
this.

Steven

unread,
Oct 14, 2010, 10:04:49 PM10/14/10
to Tobias Matt, golang-nuts
The reason is the following:
[For assignments] Each left-hand side operand must be addressable, a map index expression, or the blank identifier.

The expression i.(T), for any interface value i and type T is a type assertion (not a map index expression or the blank identifier). So we have to check if its addressable:

[An expression is addressable if it 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.

A type assertion is none of the above, so the expression i.(T) is not addressable.

If T is a struct type S, then i.(S).f, where f is a field of S, is a field selector of the non-addressable struct operand i.(S), and thus isn't addressable. It also isn't a variable or a blank identifier, so it doesn't meet the requirements to be the left-hand side operand of an assignment.

If T is a pointer type *S such that S is a struct type, then i.(*S).f is shorthand for (*i.(*S)).f, where f is a field of S, by the following rule:

If x is a pointer to a struct, x.y is shorthand for (*x).y;

i.(*S) is a pointer to a struct, so this rule applies. *i.(*S) is a pointer indirection, so it is an addressable struct, so i.(*S)).f is a field selector of an addressable struct operand, and is therefore addressable, which makes it qualify to be the left-hand operand in an assignment.

Since i.(*S).f is shorthand for  (*i.(*S)).f, it also qualifies to be the left-hand operand in an assignment.

So we have show that i.(S).f cannot be assigned to, and that i.(*S).f can be assigned to. If we substitute the interface value m["Line3"] for i, the struct type line for S, and field content for f, we get:

m["Line3"].(line).content cannot be assigned to
m["Line3"].(*line).content can be assigned to

I hope this is clear :)

Steven

unread,
Oct 14, 2010, 10:10:08 PM10/14/10
to Tobias Matt, golang-nuts
Where it says:
so i.(*S)).f is a field selector of an addressable struct operand

it should say
so (*i.(*S)).f  is a field selector of an addressable struct operand

Sorry for any inconvenience. It is fixed below:

The reason is the following:
[For assignments] Each left-hand side operand must be addressable, a map index expression, or the blank identifier.

The expression i.(T), for any interface value i and type T is a type assertion (not a map index expression or the blank identifier). So we have to check if its addressable:

[An expression is addressable if it 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.

A type assertion is none of the above, so the expression i.(T) is not addressable.

If T is a struct type S, then i.(S).f, where f is a field of S, is a field selector of the non-addressable struct operand i.(S), and thus isn't addressable. It also isn't a variable or a blank identifier, so it doesn't meet the requirements to be the left-hand side operand of an assignment.

If T is a pointer type *S such that S is a struct type, then i.(*S).f is shorthand for (*i.(*S)).f, where f is a field of S, by the following rule:

If x is a pointer to a struct, x.y is shorthand for (*x).y;

i.(*S) is a pointer to a struct, so this rule applies. *i.(*S) is a pointer indirection, so it is an addressable struct, so (*i.(*S)).f is a field selector of an addressable struct operand, and is therefore addressable, which makes it qualify to be the left-hand operand in an assignment.

Steven

unread,
Oct 15, 2010, 12:55:56 AM10/15/10
to Tobias Matt, golang-nuts
Grarr! Proof reading sometimes only works a few hours later :(

Where it says:
 It also isn't a variable or a blank identifier, so it doesn't meet the requirements to be the left-hand side operand of an assignment.

It should say:
It also isn't a map index expression or the blank identifier, so it doesn't meet the requirements to be the left-hand side operand of an assignment.

That, and a few minor fixes below. It isn't all that important, since I think I got the point across, but I also don't like leaving it wrong for someone to be confused by :P 

On Thu, Oct 14, 2010 at 10:04 PM, Steven <stev...@gmail.com> wrote:
The reason is the following:
[For assignments,] Each left-hand side operand must be addressable, a map index expression, or the blank identifier.

The expression i.(T), for any interface value i and type T, is a type assertion (not a map index expression or the blank identifier). So we have to check if it's addressable:

[An expression is addressable if it 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.

A type assertion is none of the above, so the expression i.(T) is not addressable.

If T is a struct type S, then i.(S).f, where f is a field of S, is a field selector of the non-addressable struct operand i.(S), and thus i.(S).f isn't addressable. It also isn't a map index expression or the blank identifier, so it doesn't meet the requirements to be the left-hand side operand of an assignment.

If T is a pointer type *S such that S is a struct type, then i.(*S).f is shorthand for (*i.(*S)).f (where f is a field of S) by the following rule:

If x is a pointer to a struct, x.y is shorthand for (*x).y;

i.(*S) is a pointer to a struct, so this rule applies. *i.(*S) is a pointer indirection, which makes the result an addressable struct. (*i.(*S)).f is therefore a field selector of an addressable struct operand, which makes it addressable, so it qualifies to be the left-hand operand in an assignment. 

Since i.(*S).f is shorthand for  (*i.(*S)).f, it also qualifies to be the left-hand operand in an assignment.

So we have shown that i.(S).f cannot be assigned to, and that i.(*S).f can be assigned to. If we substitute the interface value m["Line3"] for i, the struct type line for S, and the field content for f, we get:

m["Line3"].(line).content cannot be assigned to
m["Line3"].(*line).content can be assigned to

I hope this is [finally] clear :)

Reply all
Reply to author
Forward
0 new messages