Тип преобразования срезов интерфейсов в 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 или строк.

636   4  

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

    Ничего не найдено.