Search code examples
gogo-github

Error trying to use an empty interface parameter for returning data


I'm trying to write a wrap around a function that uses an interface{} parameter to return data, by adding cache.

My problem is that once I have a valid interface{} I don't know how to assign it to be returned in the parameter. The wrapped call is (github.Client) .Do in github API client and the problem hit me when I tried to add caching with go-cache

This somewhat my function

func (c *cachedClient) requestAPI(url string, v interface{}) error {
    x, found := c.cache.Get(url)
    if found {                        // Found in cache code
        log.Printf("cached: %v", x)
        v = x // HERE: this do not work. x contains a valid copy of what I want but how do I store it in v?
        return nil
    }
    req, _ := c.githubClient.NewRequest("GET", url, nil)    // not found I cache, request it
    res, err := c.githubClient.Do(*c.context, req, v)
    if err != nil {
        return err
    }
    if res.StatusCode != 200 {
        return fmt.Errorf("Error Getting %v: %v", url, res.Status)
    }
    c.cache.Add(url, v, cache.DefaultExpiration)   // store in cache
    return nil    // once here v works as expected and contain a valid item
}

It fails when has to return a cached value when I try to use it like this:

// Some init code c is a cachedClient 
i := new(github.Issue)

c.requestAPI(anAPIValidURLforAnIssue, i)
log.Printf("%+v", i)    // var i correctly contains an issue from the github api

o := new(github.Issue)
c.requestAPI(anAPIValidURLforAnIssue, o)
log.Printf("%+v", o)  // var o should have been get from the cache but here is empty

So basically my problem is that when I correctly recover a cached item it is good but I can not store it in the parameter meant to be used to store it. I can not work with subclasses because the call I'm wrapping is using an interface{} already. And I can not move it to return values because you can't return a generic interface. How do I make the interface{} x be stored in v to have it available outside?


Solution

  • To archive what you want you need to use a bit of reflection magic. Please try to replace v = x with next code snippet:

    reflect.ValueOf(v).Elem().Set(reflect.ValueOf(x).Elem())
    

    Note from OP: I had to add the last .Elem() to make this work.

    NOTE: in the call of the requestAPI method you should use a pointer to the value: let's say the cached value is of type int. Then you should call requestAPI like:

    var dst int // destination of the cached value or a newly retrieved result
    cc.requestAPI(url, &dst)