如何将 *interface{} 切片作为参数传递?

4

我想在sql包中使用Scan()函数,但列数和参数数会在运行时发生变化。这是Scan()的签名:

func (rs *Rows) Scan(dest ...interface{}) error

根据文档,*interface{}Scan() 接受的类型之一。因此,我想创建一个 []*interface{} 切片,并作为参数进行扩展。
以下是我认为应该有效的代码:
func query(database *sql.DB) {
    rows, _ := database.Query("select * from testTable")

    for rows.Next() {
        data := make([]*interface{}, 2)
        err := rows.Scan(data...) // Compilation error
        fmt.Printf("%v%v\n", *data[0], *data[1])
        if err != nil {
            fmt.Println(err.Error())
        }
    }
}

编译失败,错误信息为cannot use data (type []*interface {}) as type []interface {} in argument to rows.Scan。我以为data...会扩展为&data[0], &data[1],但显然不是这样的。我不理解错误信息。 *interface{}类型与interface{}类型兼容,那么为什么不能将指向接口类型的切片进行扩展引用呢?

以下代码可以工作:

func query(database *sql.DB) {
    rows, _ := database.Query("select * from testTable")

    for rows.Next() {
        data := make([]*interface{}, 2)
        err := rows.Scan(&data[0], &data[1]) // Only changed this line
        fmt.Printf("%v%v\n", *data[0], *data[1]) // Outputs "[48][116 101 120 116]"
        if err != nil {
            fmt.Println(err.Error())
        }
    }
}

然而,我无法使用此代码,因为在编译时无法确定列数。如何编写此代码,以便我可以传递变量数量的*interface{}rows.Scan()


2
为什么你想要[]*interface{}而不是[]interface{} - Jonathan Hall
我认为 []*interface{} 最接近答案。我会编辑我的问题以添加这个细节。 - MakotoE
1
指向接口的指针几乎从来不是正确的方法。 - Jonathan Hall
2个回答

6

首先,不要使用[]*interface{}指向接口的指针切片,而是应该使用[]interface{}指向指针接口。这两者是不同的。只需要创建一个接口切片,其中每个元素都是指向具体类型的指针即可。

以下是示例代码:

var x int
var s string

data := []interface{}{&x, &s}

rows.Scan(data...)

关于使用...扩展运算符的说明。

以下是一些相关问题,可以更好地解释:

golang:结构体切片与实现该接口的接口切片不相等?

无法将[] string转换为[] interface {}


1

如果你真的想通过[]*interface{}(也许你不知道输出的具体类型)传递数据,那么你必须先将每个*interface{}包装在一个interface{}中:

values := make([]interface{}, columnsCount)
for i := range values {
    values[i] = new(interface{})
}

将个人值传递到...interface{}参数中时,这些值会自动包装在interface{}中,但就像[]int...不能满足...interface{}一样,[]*interface{}...也不能。


如果你对查询一无所知,这个很好用。谢谢! - Thomas Fankhauser

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接