QlliOlli
QlliOlli

Reputation: 647

Go and custom struct type in another struct

I'm struggling to understand how to save a custom struct in another struct (amongst great many other things). Currently my code looks like this:

type dogs struct {
  bleeh string
  blaah string
  bluuh string
}

type Stuff struct {
  collection      *mgo.Collection
  //myAnimalStruct what type comes here?
}

func NewStuff(c *mgo.Collection) *Stuff {
  return &Stuff{
    collection: c
  }
}

func getAll(s *Stuff) interface{} {
  collection = s.collection
  var results []dogs
  err := collection.Find(bson.M{}).All(&results)
  if err != nil {
    panic(err)
  }
  return results
}

Now, I would like to get rid of that var results []dogs in getAll function. Instead, I would like to get that []dogs bit from my Stuff struct somehow, but I can't figure out how.

this is how I call this function:

func getMeDogs(w http.ResponseWriter, r *http.Request) interface{} {
  collection = Collection("animals")
  s := NewStuff(collection)
  return getAll(s)
}

So how could I do something like s := NewStuff(collection, dogs) to my Stuff struct without declaring it as a dog type in Stuff (it could be anything, in another function it could be cats for all I know...)?

The point is that I want to reuse this getAll function for whatever other types, instead of making nearly identical getAll function for all of my 63 animals. Meow.

Upvotes: 1

Views: 1957

Answers (1)

Simon Fox
Simon Fox

Reputation: 6425

You can store a prototypical value of the type in Stuff and use reflection to create a pointer to a value of that type.

type Stuff struct {
    collection  *mgo.Collection
    v           interface{}   // the prototype value
}

func NewStuff(c *mgo.Collection, v interface{}) *Stuff {
    return &Stuff{
      collection: c,
      v: v,
    }
}

func getAll(s *Stuff) (interface{}, error) {
   p := reflect.New(reflect.TypeOf(s.v))
   if err := s.collection.Find(bson.M{}).All(p.Interface()); err != nil {
      return nil, err
   }
   return p.Elem().Interface(), nil
}

To construct a Dog collection:

s := NewStuff(collection, []Dog{})

Some people will say that reflection is slow. That's true, but in this case the cost is small compared to the cost of executing Find().All(). The call to Find().All() sends a request to the database server and waits for the response. The response from the server is unpacked using Mgo's BSON decoder. The BSON decoder makes heavy use of reflection.

Upvotes: 3

Related Questions