【发布时间】:2020-04-24 11:14:38
【问题描述】:
我在 Go 中编写了一个简单的 API,使用 github.com/gin-gonic/gin。它由数据库支持,我使用github.com/jinzhu/gorm 来查询和更新数据库。
我有许多端点几乎使用相同的代码从数据库中获取数据,因此我尝试创建一个适用于我所有数据库类型的单个 retrieve 方法。
例如,我有以下结构:
type Image struct {
gorm.Model
Name string
Filename string
}
我的路由的API声明如下:
func (v1 *ApiV1) getImage(c *gin.Context) {
var images []db.Image
httpStatus := v1.retrieve("name", c.Param("name"), &images)
c.JSON(
httpStatus,
images,
)
}
可以看出,我正在尝试将指针传递给我希望查询填充的结构。由于数据库中的不同类型需要几个不同的端点,我想让retrieve 方法可重用,所以我用这个签名创建了它:
func (v1 *ApiV1) retrieve(fieldName string, name string, returnObject *[]interface{}) int {
// create var to hold the http status
var httpStatus int = http.StatusOK
// determine if a name has been passed
if name == "" {
v1.app.DB.Find(returnObject)
} else {
// look for the name in the database
v1.app.DB.Where(fieldName+" = ?", name).First(returnObject)
// if the name cannot be found then set the status to 404
if len(*returnObject) == 0 {
httpStatus = http.StatusNotFound
}
}
return httpStatus
}
为了尝试使其“通用”,我使用*[]interface{} 作为returnObject 的类型。 (这个名字有点用词不当,因为它是指针,但它很容易理解某些东西会被更新)。
但是,当我运行它时,我收到以下错误:
cannot use &images (type *[]db.Image) as type *[]interface {} in argument to v1.retrieve
我完全理解 Go 是一种严格类型化的语言,但由于我希望能够重用我的 retrieve 函数,我需要能够传入指向 Gorm 可以填充的切片(任何类型)的指针当查询运行时。
我目前正在从一个版本中重构它,该版本确实具有用于检索每个 API 端点的单独函数,但显然这是低效的并且使得执行任何重构变得更加困难。
那么我要问的可能吗?
【问题讨论】:
-
将 returnObject 类型更改为
interface{}。至于类型错误[]Tis not[]I更是如此*[]Tis not*[]I。这两种类型都没有相同的基础类型,因此您甚至无法使用转换表达式T(v)将一种转换为另一种。 golang.org/doc/faq#convert_slice_of_interface -
请注意,如果
T实现I,则可以将T用作I,但这并不意味着您也可以将[]T用作[]I因此您不能将[]interface{}用作any 切片类型的包罗万象,那是行不通的。 -
@mkopriva 谢谢。这让我意识到我做的不对。我已经修改了我的代码并发布了答案。