英文:
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
fromgetValuesFromUser()
(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...)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论