Тип конвертирующих фрагментов интерфейсов в 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.