Reputation: 38590
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.
Upvotes: 18
Views: 8416
Reputation: 39
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...)
Upvotes: -1
Reputation: 116
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:
[]string
from getValuesFromUser()
(i.e. pass still unadorned []interface{})
,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.
Upvotes: 1
Reputation: 2197
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() }
Upvotes: 12