切片和界面操作

I have recently started programming with Go on Google App Engine and I have run into a road block. I come from Java land so it's been a slight struggle to adapt to Go.

I want to have a method that allows me to pass in a pointer to a slice that I can then pass into the datastore.GetAll call to retrieve the results. I then want to iterate through the results and use an assertion to cast as a specific interface (Queryable) in order to call a method Map().

Initially, I had this functioning properly:

func (s ProjectService) RunQuery(context context.Context, q *datastore.Query, projects *[]Project) error {
    keys, err := q.GetAll(context, projects)
    if err != nil {
        return err
    }

    for i, key := range keys {
        (*projects)[i].Id = key.Encode()
        (*projects)[i].CompanyId = (*projects)[i].Company.Encode()
    }
    return nil
}

I want to have a more generic method that can be applied to any entity that implements a Queryable interface. The idea is to have a hook that allows me to perform some post processing after retrieving the results. I've looked into the ProperyLoadSaver interface however I have no access to the actual key that is associated to the entity. I would like to store the string representation of the datastore.Key in the entity.

This is the Queryable interface:

type Queryable interface {
    Map(*datastore.Key) error
}

Here's an example entity that I am persisting to the GAE store:

type Camera struct {
    Id        string `datastore:"-"`
    ProjectId string `datastore:"-"`
    Name      string
    Project   *datastore.Key `json:"-"`
    Active    bool
    Timestamp Timestamp
}

// Implement Queryable interface. Let me perform any additional mapping
func (c *Camera) Map(key *datastore.Key) error {
    c.Name = "Maybe do other things here"
    c.Id = key.Encode()
    return nil
}

The idea is to have something like the snippet below.

func (c Crud) RunQuery(context context.Context, q *datastore.Query, entities interface{}) error {
    keys, err := q.GetAll(context, entities)    
    v := reflect.ValueOf(entities)
    dv := v.Elem()

    for i, key := range keys {          
// I left this in to show that this worked however this won't let me enforce the interface contract
//dv.Index(i).FieldByName("Id").Set(reflect.ValueOf(key.Encode())) 

        entity := dv.Index(i).Interface().(Queryable)
        entity.Map(key)    
    }

    return err
}

However, when this executes, it panics with the following:

PANIC: interface conversion: entity.Camera is not entity.Queryable: missing method Map goroutine 9 [running]:

Just as a note, I realize the appropriate way to perform an assertion is to do if as, ok := elem.(Type); ok {} but I just wanted to see what the error was

I am guessing I am getting this error because I have defined my parameter with a pointer receiver func (c *Camera) Map(key *datastore.Key) error and not func (c Camera) Map(key *datastore.Key) error However, I want to modify the actual value.

Where am I going wrong with this? Is my Java-ness showing?

Being that I am very new to Go, I may be approaching this completely wrong.

Because the method is on a pointer receiver (as it should be), use the address of the slice element:

entity := dv.Index(i).Addr().Interface().(Queryable)

An alternative approach is to use a slice of pointers for the result:

var result []*Camera
err := c.RunQuery(ctx, q, &result)

The code can be written to work with both []Camera or []*Camera as follows:

var queryableType = reflect.TypeOf((*Queryable)(nil)).Elem()
needAddr := !dv.Type().Implements(queryableType)

...

var entity Queryable
if needAddr {
    entity = dv.Index(i).Addr().Interface().(Queryable)
} else {
    entity = dv.Index(i).Interface().(Queryable)
}