Подтвердить что ты не робот

Передать строковый фрагмент в переменный пустой параметр интерфейса

Пакет, который я использую, gosqlite, имеет метод с переменным параметром, где его тип - пустой интерфейс.

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

Я могу назвать это прекрасно, если явно передать отдельные параметры:

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

Однако, поскольку переменный параметр соответствует заполнителям в операторе in моего оператора SQL select, количество этих заполнителей неизвестно во время компиляции, но динамически изменяется во время выполнения в зависимости от того, что делает пользователь, Например. В итоге я получаю SQL, похожий на следующий, если пользователь вводит четыре значения:

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

Поэтому, естественно, я хотел бы вызвать метод Exec с помощью фрагмента строк:

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

Это не компилируется. Я могу обойти эту проблему, создав пустой срез интерфейса и скопировав ссылки:

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

И это прекрасно работает, но это немного неуклюже. Мне было интересно, есть ли какой-нибудь трюк, чтобы передать values непосредственно этой функции или, если это не так, более простой способ преобразования среза строки в пустой интерфейс?

Большое спасибо.

4b9b3361

Ответ 1

Невозможно передать []string непосредственно в параметр ...interface{}. Для этого требуется линейная копия времени (с n + 1 выделениями!). Если бы язык спрятал это от вас, это была бы значительная скрытая стоимость. Как правило, передача среза в переменный аргумент просто передает срез в функцию.

Как и для других способов сделать это, вы можете сделать его более чистым, написав функцию, которая принимает []string и возвращает соответствующий []interface{}. Разумеется, вам нужно будет записать его снова для каждого преобразования []T[]interface{}, которое вы хотите сделать, но его довольно короткая функция, и все эти изменения являются сигнатурой. Вы можете использовать отражение, которое поставляется со встроенной стоимостью исполнения, чтобы сделать функцию "generic", например, в:

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

Ответ 2

У меня нет ответа. И я не думаю, что есть один, поскольку даже встроенная и переменная копия и добавление имеют тот же (или совместимый конкретный) тип элемента "болван", но у меня есть два очевидных предложения:

  • не возвращать []string из getValuesFromUser() (т.е. передавать все еще без сохранения []interface{}),
  • в конце другого типа завершает обращение к statement.Exec() с преобразованием func с []string в []interface{}.

Или на том же, третье, очевидное примечание простирается type statement с Exec(args ...string).

P.S. Я не делал никаких тестов самостоятельно, но я не думаю, что такое преобразование очень дорого, поскольку интерфейс {} похож на ссылочный тип, и компилятор, вероятно, делает некоторые грязные трюки за занавеской... тогда опять же, возможно, нет, хотя, Я тоже был бы рад узнать о фактическом решении.