使用函数通过方法满足接口

Bri*_*ner 3 go

我正在尝试编写一个方法,它将返回一个可以满足json.Marshaler接口的函数.我的理由是提供结构的不同表示.也许我接近这完全错了.

func (api *Api) SiteList(c *gin.Context) {
    var sites []db.Site
    if err := api.db.Find(&sites).Error; err != nil {
    }
    var payload []json.Marshaler
    for _, site := range sites {
        payload = append(payload, site.ToApi())
    }
    c.JSON(http.StatusOK, payload)
}
Run Code Online (Sandbox Code Playgroud)

我从这个函数得到的结果是列表中正确的项目数,但每个项的值相同:

[
    {
        "key": "NZ7LCA9HQN3", 
        "name": "autumn-waterfall-1573"
    }, 
    {
        "key": "NZ7LCA9HQN3", 
        "name": "autumn-waterfall-1573"
    }, 
    {
        "key": "NZ7LCA9HQN3", 
        "name": "autumn-waterfall-1573"
    }, 
    {
        "key": "NZ7LCA9HQN3", 
        "name": "autumn-waterfall-1573"
    }, 
    {
        "key": "NZ7LCA9HQN3", 
        "name": "autumn-waterfall-1573"
    }
]
Run Code Online (Sandbox Code Playgroud)

最后,这是ToApi实现:

type EncoderFunc func() ([]byte, error)

func (fn EncoderFunc) MarshalJSON() ([]byte, error) {
    return fn()
}

func (site *Site) ToApi() json.Marshaler {
    return EncoderFunc(func() ([]byte, error) {
        var payload public.Site
        payload.Name = site.Name
        payload.Key = site.Key
        data, err := json.Marshal(payload)
        if err != nil {
            return nil, err
        }
        return data, nil
    })
}
Run Code Online (Sandbox Code Playgroud)

Ain*_*r-G 5

这似乎是一个经典的封闭陷阱.有一个相关的 FAQ部分.

基本上,site在你的for循环中每次都有相同的地址.您的所有功能都将通过此地址关闭.因此,当您在for循环之后对其进行评估时,它将重复调用MarshalJSON相同(最后)地址上的值.您可以通过在每次迭代中创建一个新值来纠正它:

for _, site := range sites {
    site := site // Perfectly legal and idiomatic.
    payload = append(payload, site.ToApi())
}
Run Code Online (Sandbox Code Playgroud)

游乐场:https://play.golang.org/p/eFujC1hEyD

Effective Go的另一篇相关文档:

错误是在Go for循环中,循环变量被重用于每次迭代,因此req变量在所有goroutine中共享.那不是我们想要的.(...)另一种解决方案是创建一个具有相同名称的新变量,如下例所示:

for req := range queue {
    req := req // Create new instance of req for the goroutine.
    sem <- 1
    go func() {
        process(req)
        <-sem
    }()
}
Run Code Online (Sandbox Code Playgroud)

这部分是关于goroutines,但显然这也适用于所有闭包.