预期切片但得到了接口。

huangapple go评论88阅读模式
英文:

Expected slice but got interface

问题

我正在使用sqlx包来进行数据库查询。我试图在我的数据库包中为所有模型创建一个全局的SELECT *

func FindAll(model interface{}, table string, db *sqlx.DB) error {
    err := db.Select(&model, "SELECT * FROM "+table)
    if err != nil {
        return fmt.Errorf("FindAll: %v", err)
    }
    return nil
}

我像这样使用它:

albums := []Album{}
err := database.FindAll(albums, "album", a.DB)

但是我得到了这个错误:expected slice but got interface

我不知道如何管理第一个模型参数,使其适用于任何模型。

英文:

I'm using sqlx package to make my database queries. I'm trying to create a global SELECT * for all my models in my database package

func FindAll(model interface{}, table string, db *sqlx.DB) error {
	err := db.Select(&model, "SELECT * FROM "+table)
	if err != nil {
		return fmt.Errorf("FindAll: %v", err)
	}
	return nil
}

And I'm using it like this

albums := []Album{}
err := database.FindAll(albums, "album", a.DB)

But I got this error : expected slice but got interface

I don't know how can I manage the first model parameter to make it works for any models

答案1

得分: 2

FindAll()函数内部,model参数的类型是interface{}。你将一个[]Album的值传递给它。它已经包装了一个切片值。

db.Select()也期望一个接口类型的参数(相同的interface{}类型)。而你正好有一个这种类型的接口值,只需直接传递即可:

err := db.Select(model, "SELECT * FROM "+table)

需要注意的是,为了让db.Select()能够修改它,它必须是一个指针,所以model应该包装一个指向切片的指针,而不仅仅是一个切片。所以像这样调用FindAll()

err := database.FindAll(&albums, "album", a.DB)

当你在需要接口类型的地方传递一个具体类型的值时,它会被包装在一个接口值中。

当你传递一个接口值时,如果它是相同的接口类型,它将按原样传递。如果它是不同的接口类型,存储在其中的具体值将被包装(重新包装)在期望的接口类型的接口值中。

当你传递&model时,你传递的是一个*interface{}类型的值,一个指向接口类型的指针。这不是一个接口类型,它是一个具体类型,所以当传递时,这个具体类型将被包装在一个接口值中。

参考相关问题:https://stackoverflow.com/questions/38829941/golang-interface-type-misunderstanding/38830638#38830638

英文:

Inside FindAll(), the model parameter is of interface{} type. You pass a value of []Album to it. It already wraps a slice value.

db.Select() also expects an argument of interface type (the same interace{} type). And you have an interface value of exactly this type, just pass it as-is:

err := db.Select(model, "SELECT * FROM "+table)

Although note that in order for db.Select() to be able to modify it, it must be a pointer, so model should wrap a pointer to a slice, not "just" a slice. So call FindAll() like this:

err := database.FindAll(&albums, "album", a.DB)

When you pass a value of concrete type when an interface type is needed, it will be wrapped in an interface value.

When you pass an interface value, if it is of the same interface type, it will be passed as-is. If it's a different interface type, the concrete value stored in it will be wrapped (re-wrapped) in an interface value of the expected interface type.

When you pass &model, you're passing a value of *interface{}, a pointer to an interface type. That's not an interface type, it's a concrete type, so this concrete type will be wrapped in an interface value when passed.

See related question: https://stackoverflow.com/questions/38829941/golang-interface-type-misunderstanding/38830638#38830638

huangapple
  • 本文由 发表于 2021年11月6日 23:44:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/69865478.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定