2012-10-05 27 views
98

Tengo curiosidad por qué go no convierte implícitamente []T en []interface{} cuando implícitamente convertirá T en interface{}. ¿Hay algo no trivial acerca de esta conversión que me falta?Escriba la conversión de segmentos de interfaces en go

Ejemplo:

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

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

go build se queja

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

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

No se puede convertir un (tipo cadena []) para escribir [] interfaz {}

Así que cada vez que tengo que hacer esta conversión (que parece venir mucho), he estado haciendo algo como esto:

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

¿hay una mejor manera de hacer esto, o funciones de la biblioteca estándar para ayudar con estas conversiones? Parece una tontería escribir 4 líneas adicionales de código cada vez que quiero llamar a una función que puede tomar una lista de, p. Ints o cadenas.

+0

por lo que definen "implícitamente convertir [] T para la interfaz [] {}" en el sentido de "asignación de un nuevo segmento y la copia de todos los elementos más". Eso es inconsistente con lo que "convierte implícitamente T a la interfaz {}", que es solo una vista del mismo valor que un tipo estático más genérico; nada se copia; y si tecleas, vuelve a afirmar que escribes T, aún obtendrías lo mismo. – newacct

+0

no estaba pensando en demasiados detalles sobre cómo se implementaría, solo que a un nivel más alto sería conveniente poder convertir fácilmente una lista completa a otro tipo como una solución para no tener tipos genéricos. – danny

+0

Buena pregunta. :) – weberc2

Respuesta

104

En Go, existe una regla general que dice que la sintaxis no debe ocultar operaciones complejas/costosas. La conversión de un string a un interface{} se realiza en O (1) vez. La conversión de un []string a un interface{} también se realiza en O (1) vez, ya que un sector sigue siendo un valor. Sin embargo, la conversión de []string a []interface{} es O (n) tiempo porque cada elemento del sector debe convertirse a interface{}.

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

No hay una función de biblioteca estándar que realice esta conversión por usted. Podría hacer uno con reflejo, pero sería más lento que la opción de tres líneas.

Ejemplo con la reflexió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 
} 

Su mejor opción es sin embargo sólo para usar las líneas de código que le dio en su pregunta:

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

podría ser más lento, pero funcionaría genéricamente con cualquier tipo de división – newacct

+0

Toda esta respuesta se aplica a 'map's también por cierto. – RickyA

+0

interfaz, no inteface. Qué truco :) – Jacob

3

Pruebe interface{} en su lugar. Para devolverlo como una porción, intente

func foo(bar interface{}) { 
    s := bar.([]string) 
    // ... 
} 
+2

Esto exige la siguiente pregunta: ¿cómo se supone que el OP debe iterar sobre 'bar' para interpretarlo como" un corte de cualquier tipo "? Tenga en cuenta que su three-liner crea una interfaz '[] {}', no '[] string' o una porción de otro tipo concreto. – kostix

+5

-1: Esto solo funciona si la barra es una cadena [], en cuyo caso, también puede escribir: 'func foo (barra [] cadena) {/ * ... * /}' – weberc2

+1

use un interruptor de tipo, o usa la reflexión como en la respuesta aceptada. – dskinner

33

Lo que se echa en falta es que T y interface{} que tiene un valor de T tiene diferentes representaciones en la memoria, por lo que no se puede convertir trivialmente.

Una variable del tipo T es solo su valor en la memoria.No hay información de tipo asociada (en Go, cada variable tiene un único tipo conocido en tiempo de compilación, no en tiempo de ejecución). Se representa en la memoria de esta manera:

  • valor

Un interface{} la celebración de una variable de tipo T se representa en la memoria como este puntero

  • para escribir T
  • valor

Volviendo a su pregunta original: ¿por qué go no convierte implícitamente []T en []interface{}?

La conversión de []T a []interface{} implicaría la creación de un nuevo segmento de valores interface {} que es una operación no trivial ya que el diseño en memoria es completamente diferente.

+2

Gracias por explicar realmente la esencia del problema – kostix

+4

Esto es informativo y está bien escrito (+1), pero no está abordando la otra parte de su pregunta: "¿Hay una manera mejor de hacer esto ..." (- 1). – weberc2