Search code examples
jsongoserverslicetype-assertion

Why does my code panic when I type assert an interface?


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?


Solution

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