Tipo de conversión de porciones de interfaces

Resuelto danny asked hace 11 años • 9 respuestas

Tengo curiosidad por saber por qué Go no convierte implícitamente []Tcuándo []interface{}se convertirá implícitamente Ta interface{}. ¿Hay algo no trivial acerca de esta conversión que me estoy perdiendo?

Ejemplo:

func foo([]interface{}) { /* do something */ }

func main() {
    var a []string = []string{"hello", "world"}
    foo(a)
}

go buildse queja

no se puede utilizar un (tipo []cadena) como tipo []interfaz {} en el argumento de la función

Y si intento hacerlo explícitamente, lo mismo: b := []interface{}(a)se queja

no se puede convertir un (tipo []cadena) al tipo []interfaz {}

Entonces, cada vez que necesito hacer esta conversión (que parece surgir con frecuencia), hago algo como esto:

b = make([]interface{}, len(a), len(a))
for i := range a {
    b[i] = a[i]
}

¿Existe una mejor manera de hacer esto o funciones de biblioteca estándar para ayudar con estas conversiones? Parece un poco tonto escribir 4 líneas adicionales de código cada vez que quiero llamar a una función que puede tomar una lista de, por ejemplo, entradas o cadenas.

danny avatar Oct 06 '12 03:10 danny
Aceptado

En Go, existe una regla general de que la sintaxis no debe ocultar operaciones complejas/costosas .

La conversión de a stringen an interface{}se realiza en tiempo O(1). La conversión de a []stringen an interface{}también se realiza en tiempo O(1), ya que un segmento sigue siendo un valor. Sin embargo, convertir a []stringen an []interface{}es un tiempo O(n) porque cada elemento del segmento debe convertirse en un interface{}.

La única excepción a esta regla es la conversión de cadenas. Al convertir a stringhacia y desde a []byteo a []rune, Go hace que O(n) funcione aunque las conversiones sean "sintaxis".

No existe una función de biblioteca estándar que realice esta conversión por usted. Sin embargo, su mejor opción es simplemente usar las líneas de código que proporcionó en su pregunta:

b := make([]interface{}, len(a))
for i := range a {
    b[i] = a[i]
}

De lo contrario, podrías crear uno con reflect, pero sería más lento que la opción de tres líneas. Ejemplo con reflexión:

func InterfaceSlice(slice interface{}) []interface{} {
    s := reflect.ValueOf(slice)
    if s.Kind() != reflect.Slice {
        panic("InterfaceSlice() given a non-slice type")
    }

    // Keep the distinction between nil and empty slice input
    if s.IsNil() {
        return nil
    }

    ret := make([]interface{}, s.Len())

    for i:=0; i<s.Len(); i++ {
        ret[i] = s.Index(i).Interface()
    }

    return ret
}
Stephen Weinberg avatar Oct 05 '2012 22:10 Stephen Weinberg

Lo que te falta es eso Ty interface{}que tiene un valor de Ttiene representaciones diferentes en la memoria, por lo que no se puede convertir de manera trivial.

Una variable de tipo Tes solo su valor en la memoria. No hay información de tipo asociada (en Go cada variable tiene un único tipo conocido en el momento de la compilación, no en el tiempo de ejecución). Se representa en memoria así:

  • valor

Una interface{}variable de tipo que contiene Tse representa en la memoria de esta manera

  • puntero para escribirT
  • valor

Entonces, volviendo a su pregunta original: ¿por qué go no se convierte implícitamente []Ta []interface{}?

[]TLa conversión []interface{}implicaría crear una nueva porción de interface {}valores, lo cual no es una operación trivial ya que el diseño en memoria es completamente diferente.

Nick Craig-Wood avatar Oct 06 '2012 08:10 Nick Craig-Wood