I have some server code that sends a request to an endpoint and receives a JSON response which is stored in an object that is of type empty interface. I have to parse out the information and store it in a slice of "Resource" objects, with Resource being an interface. The JSON data in my case represents a "Position" object, which satisfies the Resource interface. So basically the code for these look like this:
// Resource interface type
type Resource interface {
// Identifier returns the id for the object
Identifier() bson.ObjectId
// Description give a short description of the object
Description() string
// Initialize should configure a resource with defaults
Initialize()
// Collection name for resource
Collection() string
// Indexes for the resources
Indexes() []mgo.Index
// UserACL returns the user access control list
UserACL() *UserACL
// IsEqual should compare and return if resources are equal
IsEqual(other Resource) bool
// Refresh should update a resource from the database
Refresh()
}
And the position model is:
// Position model
type Position struct {
ID bson.ObjectId `json:"id" bson:"_id,omitempty" fake:"bson_id"`
Title string `json:"title" bson:"title" fake:"job_title"`
Summary string `json:"summary" bson:"summary,omitempty" fake:"paragraph"`
IsCurrent bool `json:"isCurrent" bson:"is_current,omitempty" fake:"bool"`
CompanyID bson.ObjectId `json:"company" bson:"company_id,omitempty" fake:"bson_id"`
UACL *UserACL `bson:"user_acl,omitempty" fake:"user_acl"`
}
// Identifier returns the id for the object
func (p *Position) Identifier() bson.ObjectId {
return p.ID
}
// Description give a short description of the object
func (p *Position) Description() string {
return fmt.Sprintf("[%v:%v]", p.Collection(), p.ID)
}
....(the other methods follow)
My endpoint is designed to retrieve a list of positions in my database, so this obviously means that the empty interface containing the JSON data contains a slice of Resources, and can't be type asserted into a slice (Go doesn't allow this) and instead done manually by iteration. So I followed through the code and isolated my problem to this:
func InterfaceSlice(slice interface{}) []Resource {
s := reflect.ValueOf(slice).Elem()
if s.Kind() != reflect.Slice {
panic("InterfaceSlice() given a non-slice type")
}
ret := make([]Resource, s.Len())
for i := 0; i < s.Len(); i++ {
r := s.Index(i)
rInterface := r.Interface()
ret[i] = rInterface.(Resource)
}
return ret
}
Everything in the above code is working just fine until
ret[i] = rInterface.(Resource)
and then my server blows up and panics. I looked through the Go documentation and as far as I can tell I should be able to type assert into Resource even though rInterface is an empty interface with Position model data because the Position type satisfies the Resource interface anyway. Am I correct in understanding this? Or is there something I'm missing?
Ok Kaedys suggested that I change r := s.Index(i) Into: r := s.Index(i).Addr()
And this did the trick, apparently the problem occurs when you use an object that is supposed to fulfill an interface, but all the methods on that object have pointer receivers. I just needed to put a pointer to the type into the interface.