Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pass string slice to variadic empty interface parameter

Tags:

go

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.

like image 936
Paul Ruane Avatar asked Nov 02 '11 02:11

Paul Ruane


2 Answers

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() } 
like image 125
SteveMcQwark Avatar answered Sep 20 '22 07:09

SteveMcQwark


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.

like image 37
ypb Avatar answered Sep 23 '22 07:09

ypb