Search code examples
structgomgo

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.


Solution

  • 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.