将字符串切片传递给可变的空接口参数

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

Pass string slice to variadic empty interface parameter

问题

我正在使用的一个包,gosqlite,有一个带有可变参数的方法,其中参数的类型是空接口

func (s *Stmt) Exec(args ...interface{}) os.Error

如果明确传递单个参数,我可以正常调用它:

statement := blah()
error := statement.Exec("hello", 3.0, true) // 正常工作

然而,由于可变参数对应于我的SQL语句的select中的in运算符中的占位符,这些占位符的数量在编译时是未知的,但在运行时根据用户的操作动态变化。例如,如果用户输入四个值,我会得到类似以下的SQL:

SELECT * FROM sky WHERE name IN (?,?,?,?)

因此,我希望能够使用字符串切片调用Exec方法:

var values []string = getValuesFromUser()
statement := createStatementWithSufficientNumberOfPlaceholders(len(values))
_ := statement.Exec(values...) // 编译器不喜欢这个

这段代码无法编译。我可以通过创建一个空接口切片并将引用复制过去来解决这个问题:

values2 := make([]interface{}, len(values))
for index, value := range values { values2[index] = value }
_ := statement.Exec(values2...) // 编译器满意但我不满意

这样可以正常工作,但感觉有点笨拙。我想知道是否有什么技巧可以直接将values传递给这个函数,或者是否有更简洁的方法将字符串切片转换为空接口切片?

非常感谢。

英文:

A package I am using, gosqlite, has a method with a variadic parameter where its type is the empty interface.

func (s *Stmt) Exec(args ...interface{}) os.Error

I can call this fine if explicitly pass individual parameters:

statement := blah()
error := statement.Exec("hello", 3.0, true) // works fine

However, as the variadic parameter corresponds to placeholders within the in operator of my SQL statement's select, the number of these placeholders is not known at compile time but dynamically changes at run time depending upon what the user is doing. E.g. I end up with SQL akin to the following if the user enters four values:

SELECT * FROM sky WHERE name IN (?,?,?,?)

So naturally I would like to call the Exec method with a slice of strings:

var values []string = getValuesFromUser()
statement := createStatementWithSufficientNumberOfPlaceholders(len(values))
_ := statement.Exec(values...) // compiler doesn't like this

This does not compile. I can get around this problem by creating an empty interface slice and copying the references over:

values2 := make([]interface{}, len(values))
for index, value := range values { values2[index] = value }
_ := statement.Exec(values2...) // compiler happy but I'm not

And this works fine but it feels a bit clunky. I was wondering if there was some trick to be able to pass values directly to this function or, failing that, a neater way of converting the string slice to an empty interface one?

Many thanks.

答案1

得分: 12

直接将[]string传递给...interface{}参数是不可能的。这样做需要进行线性时间复制(带有n + 1次分配!)。如果语言将此隐藏起来,那将是一个显著的隐藏成本。通常,将切片传递给可变参数只是将切片传递到函数中。

至于其他方法,您可以通过编写一个接受[]string并返回相应的[]interface{}的函数来使其更清晰。当然,您将不得不为每个想要进行的[]T -> []interface{}转换再次编写该函数,但它是一个相当简短的函数,唯一改变的是签名。您可以使用反射来使该函数“通用”,但这会带来固有的运行时成本,例如:

valuesVal := reflect.ValueOf(values)
...
for i := range values2 { values2[i] = valuesVal.Index(i).Interface() } 
英文:

There is no way to pass a []string directly to a ...interface{} parameter. Doing this requires a linear time copy (with n + 1 allocations!). If the language hid this from you, it would be a significant hidden cost. Normally, passing a slice to a variadic argument just passes the slice into the function.

As for other ways of doing this, you could make it cleaner by writing a function that takes a []string and returns the corresponding []interface{}. Of course, you'll have to write it again for each []T -> []interface{} conversion you want to do, but its a rather short function, and all that changes is the signature. You could use reflection, which comes with an inherent runtime cost, to make the function "generic", such as in:

valuesVal := reflect.ValueOf(values)
...
for i := range values2 { values2[i] = valuesVal.Index(i).Interface() } 

答案2

得分: 1

我没有答案。我不认为有一个答案,因为即使内置的和可变参数的_copy_和_append_具有相同(或兼容的具体)元素类型“blockhead”,但我有两个明显的建议:

  • 不要从getValuesFromUser()返回[]string(即传递仍未装饰的[]interface{}),
  • 另一种方法是在调用statement.Exec()时使用一个将[]string转换为[]interface{}的_func_。

或者在同一个明显的注释中,使用type statement扩展Exec(args ...string)

P.S. 我自己没有进行任何基准测试,但我不认为这种类型的转换非常昂贵,因为interface{}感觉像是一个引用类型,编译器可能在幕后做了一些不可告人的把戏...不过也许不是,不过,我也很乐意了解一个真正的解决方案。

英文:

I don't have an answer. And I don't suppose there is one since even built-in and variadic copy and append have the same (or compatible concrete) element type "blockhead", but I have two obvious suggestions:

  • do not return []string from getValuesFromUser() (i.e. pass still unadorned []interface{}),
  • on the other type end wrap calls to statement.Exec() with a func making []string to []interface{} conversion.

Or on the same, third, obvious note extend type statement with Exec(args ...string).

P.S. I haven't made any benchmarks myself but I don't think this kind of conversion is highly expensive as interface{} feels like a reference type and compiler is probably doing some dirty trickstery behind the curtain... then again perhaps not, though, I'd be happy, too, to learn of an actual solution.

答案3

得分: -1

你需要像这样传递一个接口{}类型的可变参数切片给这个方法。

var paramArray []interface{}
paramArray = append(paramArray, "test1")
paramArray = append(paramArray, "test2")
varargsFunc(paramArray...)
英文:

You need to pass a varargs slice of interface{} type like this to the method.

var paramArray []interface{}
paramArray = append(paramArray, "test1")
paramArray = append(paramArray, "test2")
varargsFunc(paramArray...)

huangapple
  • 本文由 发表于 2011年11月2日 10:12:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/7975095.html
匿名

发表评论

匿名网友

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

确定