Converting or accessing arrays of interface{}?

3,613 views
Skip to first unread message

dpapathanasiou

unread,
Aug 22, 2012, 2:08:00 PM8/22/12
to golang-nuts
I'm having trouble calling a function which returns []interface{} and
converting or accessing those results as the underlying struct they're
supposed to be.

This came up while using mgo, the mongodb driver for go, but I realize
it's more of an issue of my understanding of go in general, so I'm
asking the question here.

Suppose I have several structs, one of which is defined as:

type Person struct {
FirstName string
LastName string
}

I also have a function which queries a database, and depending on the
parameter input, can return a Person, among other types of structs:


func DoQuery(collection string, query interface{}) (result
[]interface{}) {
// run the query and put the results into &result
return result
}

When I run it like this:

for _, person := range DoQuery(bson.M{"lastName": "Jones"}) {
fmt.Println(person)
}

I don't get back a Person struct as I expect, but a map object, like
this, for each person in the result set:

map[FirstName:Alex LastName:Jones]

If I try to access person.LastName or person.FirstName within the loop
above, the program won't compile, and I get this kind of error:

person.FirstName undefined (type interface {} has no field or method
FirstName)

After reading the section on reflection (http://golang.org/doc/
articles/laws_of_reflection.html), I tried rewriting the loop this
way:

for _, person := range DoQuery(bson.M{"lastName": "Jones"}) {
p := reflect.ValueOf(&person).Elem()
for i := 0; i < p.NumField(); i++ {
fmt.Print(p.Field(i))
}
fmt.Println()
}

While that compiled, it gave me a runtime panic:

panic: result argument must be a slice address

And when I tried to take a slice, I started getting compiler errors:

invalid operation: person[x] (index of type interface {})

While I could avoid all this by making the return type of the
DoQuery() function to be (result []Person), it doesn't give me the
flexibility I want in terms of being able to pass different input
parameters to get different structs.

I also tried forcing the return type like this, but it wouldn't
compile:

var result []RelatedPerson
result = DoQuery("relatedPerson", bson.M{"lastName":
"Jones"})
for _, person := range result {
fmt.Println(person)
}

So what am I not getting about handling arrays of interface{}?

Matt Kane's Brain

unread,
Aug 22, 2012, 2:16:05 PM8/22/12
to dpapathanasiou, golang-nuts
On Wed, Aug 22, 2012 at 2:08 PM, dpapathanasiou
<denis.pap...@gmail.com> wrote:
> person.FirstName undefined (type interface {} has no field or method
> FirstName)

You would need to do a type assertion for this.

person.(Person).FirstName

But it looks like the array is not populated with such structures, and
that type assertion would panic.

--
matt kane's brain
http://hydrogenproject.com

dpapathanasiou

unread,
Aug 22, 2012, 4:08:13 PM8/22/12
to golang-nuts
Thanks for your reply.

On Aug 22, 2:16 pm, "Matt Kane's Brain" <mkb-
pr...@hydrogenproject.com> wrote:
[snip]
> You would need to do a type assertion for this.
>
> person.(Person).FirstName

Right, this is what I didn't understand; I'll read more about type
assertions.

> But it looks like the array is not populated with such structures, and
> that type assertion would panic.

Yes, mgo actually returns a bson.M type (http://go.pkgdoc.org/
labix.org/v2/mgo/bson#M) which is a map, so my type assertion should
be:

person.(bson.M)

and then I need to inspect the map to see if it contains the
attributes found in the Person struct versus another struct.

While it's not as automatic as I'd like it, it's manageable.
>
> --
> matt kane's brainhttp://hydrogenproject.com
Reply all
Reply to author
Forward
0 new messages