Тип конвертирующих фрагментов интерфейсов в Go
Мне любопытно, почему Go неявно преобразует []T
в []interface{}
когда он неявно преобразует T
в interface{}
. Есть ли что-то нетривиальное в этом преобразовании, которое я пропускаю?
Пример:
func foo([]interface{}) { /* do something */ }
func main() {
var a []string = []string{"hello", "world"}
foo(a)
}
go build
жалуется
нельзя использовать (строку типа []) как интерфейс типа [] {} в аргументе функции
И если я попытаюсь сделать это явно, то же самое: b := []interface{}(a)
жалуется
невозможно преобразовать (строку типа []) в интерфейс типа [] {}
Таким образом, каждый раз, когда мне нужно сделать это преобразование (которое, кажется, очень много), я делал что-то вроде этого:
b = make([]interface{}, len(a), len(a))
for i := range a {
b[i] = a[i]
}
Есть ли лучший способ сделать это, или стандартные функции библиотеки, чтобы помочь с этими преобразованиями? Кажется глупым писать 4 дополнительные строки кода каждый раз, когда я хочу вызвать функцию, которая может принимать список, например, целые числа или строки.
Ответы
Ответ 1
В Go существует общее правило, согласно которому синтаксис не должен скрывать сложные/дорогостоящие операции. Преобразование a string
в interface{}
выполняется в O (1) раз. Преобразование a []string
в interface{}
также выполняется в O (1) раз, так как срез остается одним значением. Однако преобразование a []string
в []interface{}
является временем O (n), потому что каждый элемент среза должен быть преобразован в interface{}
.
Единственным исключением из этого правила является преобразование строк. При преобразовании string
в и из []byte
или []rune
, Go делает O (n), даже если преобразования являются "синтаксисом".
Нет стандартной функции библиотеки, которая сделает это преобразование для вас. Вы можете сделать один с отражением, но он будет медленнее, чем три варианта линии.
Пример с отражением:
func InterfaceSlice(slice interface{}) []interface{} {
s := reflect.ValueOf(slice)
if s.Kind() != reflect.Slice {
panic("InterfaceSlice() given a non-slice type")
}
ret := make([]interface{}, s.Len())
for i:=0; i<s.Len(); i++ {
ret[i] = s.Index(i).Interface()
}
return ret
}
Самый лучший вариант - просто использовать строки кода, которые вы указали в своем вопросе:
b := make([]interface{}, len(a))
for i := range a {
b[i] = a[i]
}
Ответ 2
Единственное, чего вам не хватает, так это того, что T
и interface{}
которые содержат значение T
имеют разные представления в памяти, поэтому не могут быть тривиально преобразованы.
Переменная типа T
- это просто ее значение в памяти. Информация о связанном типе отсутствует (в Go каждая переменная имеет один тип, известный во время компиляции, а не во время выполнения). Он представлен в памяти так:
interface{}
содержащий переменную типа T
, представлен в памяти следующим образом
- указатель на тип
T
- значение
Итак, возвращаясь к исходному вопросу: почему go не выполняет неявное преобразование []T
в []interface{}
?
Преобразование []T
в []interface{}
может включать создание нового среза значений interface {}
что является нетривиальной операцией, поскольку макет в памяти совершенно другой.
Ответ 3
Вот официальное объяснение: https://github.com/golang/go/wiki/InterfaceSlice
var dataSlice []int = foo()
var interfaceSlice []interface{} = make([]interface{}, len(dataSlice))
for i, d := range dataSlice {
interfaceSlice[i] = d
}
Ответ 4
Попробуйте interface{}
. Чтобы отбросить фрагмент, попробуйте
func foo(bar interface{}) {
s := bar.([]string)
// ...
}
Ответ 5
Скрытый interface{}
в любой тип.
Синтаксис:
result := interface.(datatype)
Пример:
var employee interface{} = []string{"Jhon", "Arya"}
result := employee.([]string) //result type is []string.