Тип преобразования срезов интерфейсов в go
мне любопытно, почему go неявно преобразует []T до []interface{} когда он будет неявно преобразовать T до interface{}. Есть ли что-то нетривиальное в этом преобразовании, что мне не хватает?
пример:
func foo([]interface{}) { /* do something */ }
func main() {
var a []string = []string{"hello", "world"}
foo(a)
}
go build жалуется
не может использовать (type []string) в качестве интерфейса типа [] {} в аргументе функции
и если я попытаюсь сделать это явно, то же самое:b := []interface{}(a) жалуется
не удается преобразовать (тип []строка) в тип [] интерфейс {}
поэтому каждый раз, когда мне нужно сделать это преобразование (которое, кажется, придумать много), я делаю что-то вроде этого:
b = make([]interface{}, len(a), len(a))
for i := range a {
b[i] = a[i]
}
есть ли лучший способ сделать это, или стандартные библиотечные функции, чтобы помочь с этими преобразованиями? Кажется глупым писать 4 дополнительные строки кода каждый раз, когда я хочу вызвать функцию, которая может принимать список, например, ints или строк.
4 ответов:
в Go существует общее правило, что синтаксис не должен скрывать сложные/дорогостоящие операции. Конвертировании
stringдоinterface{}делается в O (1) раз. Конвертировании[]stringдоinterface{}также делается в O (1) времени, так как срез по-прежнему одно значение. Однако, преобразование[]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] }
то, что вам не хватает, это
Tиinterface{}, который содержит значениеTимеют разные представления в памяти, поэтому не могут быть тривиально преобразованы.переменной типа
T- Это просто его значение в памяти. Нет связанной информации о типе (в Go каждая переменная имеет один тип, известный во время компиляции, а не во время выполнения). Он представлен в памяти следующим образом:
- стоимостью
An
interface{}держа переменная типаTпредставляется в памяти вот так
- указатель на тип
T- стоимостью
Итак, возвращаясь к вашему первоначальному вопросу: почему go неявно преобразует
[]Tдо[]interface{}?преобразование
[]Tдо[]interface{}будет включать в себя создание нового фрагментаinterface {}значения, которые являются нетривиальной операцией, поскольку макет в памяти полностью отличается.
вот официальное объяснение: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 }
попробовать
interface{}вместо. Чтобы отбросить назад как кусочек, попробуйтеfunc foo(bar interface{}) { s := bar.([]string) // ... }
Comments