尝试使用空接口参数返回数据时出错

Error trying to use an empty interface parameter for returning data

我正在尝试通过添加缓存来编写一个将 interface{} 参数用于 return 数据的函数。

我的问题是,一旦我有了一个有效的 interface{},我就不知道如何在参数中将其指定为 returned。封装的调用是 (github.Client) .Do in github API client and the problem hit me when I tried to add caching with go-cache

这有点像我的功能

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
}

当我尝试像这样使用它时必须 return 缓存值时失败:

// 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

所以基本上我的问题是,当我正确恢复缓存项时它很好,但我不能将它存储在用于存储它的参数中。我无法使用子类,因为我正在包装的调用已经在使用 interface{}。而且我不能将它移动到 return 值,因为你不能 return 通用接口。如何将 interface{} x 存储在 v 中以使其在外部可用?

根据某些假设,比如您正在将 json 数据存储在您的缓存中,下面是我将尝试的方法。错误未处理。

package main

import (
    "encoding/json"
    "fmt"
)

type Data struct {
    Name string
}

func main() {
    var d Data
    requestAPI(&d)
    fmt.Println(d)
}

func requestAPI(v interface{}) {
    var cache_res interface{} = []byte("{\"Name\":\"CCC\"}")
    //assume you got cache_res from cache
    x, _ := cache_res.([]byte)
    _ = json.Unmarshal(x, &v)
}

其实上面也是githubClient.Do也在做的。它检查 v 是否满足 io.Writer 接口,如果满足,则将数据写入 v。如果不满足,则将 json 解编为 v,如上所示。所以同样可以从缓存中完成。

检查这里: https://github.com/google/go-github/blob/v32.1.0/github/github.go#L586


如果缓存对象是特定的,则可以使用以下内容。您不处理空接口{},因为您应该能够将您的特定类型作为 v 传递给 c.githubClient.Do。由于它使用 json 包,它将检测类型信息并相应地填充值进去。 假设您存储 type Data struct

在下面的代码中删除了其他细节,例如条件检查是否缓存和错误处理

package main

import (
    "fmt"
)

type Data struct {
    Name string
}

func main() {
    var d Data
    requestAPI(&d)
    fmt.Println(d)
}

func requestAPI(v *Data) {
    var cache_res interface{} = Data{"CCC"}
    //assume you got cache_res from cache
    x, _ := cache_res.(Data)
    *v = x

    //in case you did not find it in cache then githubClient.Do should unmarshal
    //contents of response body into v *Data if Data fields match that of json
    //res, err := c.githubClient.Do(*c.context, req, v)
}

要存档您需要的内容,您需要使用一些反射魔法。 请尝试用下一个代码片段替换 v = x

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

OP 的注释:我必须添加最后一个 .Elem() 才能完成这项工作。

注意:在 requestAPI 方法的调用中,您应该使用指向该值的指针: 假设缓存值的类型为 int。那么你应该像这样调用 requestAPI

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