Assignment to an interface{} argument vs. return of an interface{} value

175 views
Skip to first unread message

john

unread,
Aug 31, 2015, 10:58:54 AM8/31/15
to golang-nuts
I'm building an appengine backend server in go, and trying to write a generic function that will get an arbitrary item from the local memory cache (using github.com/pmylund/go-cache) or fall through to appengine's memcache.
The problem is that each caching library is using a different getter, and it's difficult to write one function that returns results from either.

The "Gob" codec in appengine's memcache accepts a pointer (via an interface{} argument), to which it decodes the item.
https://godoc.org/google.golang.org/appengine/memcache#Codec.Get

go-cache's getter returns an interface{} value to be type-asserted by the caller.
http://godoc.org/github.com/pmylund/go-cache#Cache.Get

My function needs to return an item in one of those ways, but I'm unable to wrap either of the getters so that it works in like the other.


Alternatives I could think of:
  • Duplicate the caching logic for every cached type (no generic function) - the current solution
  • Store the values in local memory using Gob, to match memcache's getter - clearly a hack
  • Use reflection, which seems like the right tool, but is complicated and I failed to construct a working example with
 
Any ideas on how to approach this?

Thank you.

Dustin

unread,
Aug 31, 2015, 11:39:53 AM8/31/15
to golang-nuts
If you're only caching things of a specific type (or a list of types), you could probably get away with just a type switch.

Otherwise, if you really need a generic implementation you'll need to use reflection.

This is a crude example, but something to start with: https://play.golang.org/p/zMkqcmyQqy

john8...@gmail.com

unread,
Sep 1, 2015, 5:51:29 AM9/1/15
to golang-nuts
Thank you.

Would that be comprehensive enough?

func intervoodoo(dst interface{}, src interface{}) error {
dstVal := reflect.ValueOf(dst)
srcVal := reflect.ValueOf(src)

if dstVal.Type().Kind() != reflect.Ptr {
return fmt.Errorf("not a pointer")
}
if !dstVal.Elem().CanSet() {
return fmt.Errorf("unsettable")
}

// Dereference
if srcVal.Type().Kind() == reflect.Ptr {
srcVal = srcVal.Elem()
}
dstVal = dstVal.Elem()

if !srcVal.Type().AssignableTo(dstVal.Type()) {
return fmt.Errorf("unassignable")
}

dstVal.Set(srcVal)

return nil
}



I'm worried about edge cases that I might be missing, mainly anything that will cause a panic.
Is there anything else that needs to be checked/done inside?

Dustin

unread,
Sep 1, 2015, 10:13:56 PM9/1/15
to golang-nuts, john8...@gmail.com
If you want to be comprehensive you should check all the calls you make and see when they panic. 

There are still cases where this code can panic, like: http://play.golang.org/p/QHEGYsVTUj

But you should really just define a contract for what kinds of things are allowed to be passed as input to this function (so you don't have to cover every possible input).
Reply all
Reply to author
Forward
0 new messages