Escriba segmentos de conversión de interfaces


194

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

Ejemplo:

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

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

go build se queja

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

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

no puede convertir una (cadena de tipo []) a la interfaz de tipo [] {}

Entonces, cada vez que necesito hacer esta conversión (que parece surgir 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 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, ints o cadenas.


entonces define "convertir implícitamente [] T a [] interfaz {}" para significar "asignar un nuevo segmento y copiar todos los elementos". Eso es inconsistente con lo que hace "convertir T implícitamente 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 escribe-afirmarlo de nuevo al tipo T, todavía obtendría lo mismo de nuevo.
newacct

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

Respuestas:


215

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

La única excepción a esta regla es convertir cadenas. Al convertir un ay stringdesde a []byteo a []rune, Go funciona O (n) a pesar de que las conversiones son "sintaxis".

No hay una función de biblioteca estándar que haga esta conversión por usted. Podrías hacer 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")
    }

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

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

    return ret
}

Sin embargo, su mejor opción es 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]
}

3
podría ser más lento, pero funcionaría genéricamente con cualquier tipo de rebanada
newacct

Toda esta respuesta se aplica a maps también por cierto.
RickyA

Esto también se aplica channelstambién.
Justin Ohms

Gracias por una explicación clara, si es posible, ¿puede agregar ref. para Converting a []string to an interface{} is also done in O(1) time?
Mayur

56

Lo que falta es eso Ty interface{}que tiene el valor de Ttener diferentes representaciones en la memoria, por lo que no se puede convertir trivialmente.

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

  • valor

Una interface{}celebración de una variable de tipo Tse representa en la memoria de esta manera

  • puntero para escribir T
  • valor

Volviendo a su pregunta original: ¿por qué ir no se convierte implícitamente []Ta []interface{}?

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


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

13

Aquí está la explicación oficial: 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
}

Buena información pero no es una explicación oficial. Es una wiki donde todos pueden editar.
Inanc Gumus

¿Cómo debo convertir []interfaceal tipo que espero como []map[string]interface{}?
Lewis Chan

8

Intenta en su interface{}lugar. Para lanzar de nuevo como rebanada, intente

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

3
Esto plantea la siguiente pregunta: ¿cómo se supone que el OP debe iterar barpara interpretarlo como "una porción de cualquier tipo"? Tenga en cuenta que su revestimiento de tres crea un []interface{}, no []stringo un trozo de otro tipo de hormigón.
kostix

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

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

OP va a tener que averiguar su tipo en tiempo de ejecución de cualquier manera. El uso de non slice interface{}hará que el compilador no se queje al pasar un argumento como en foo(a). Él podría usar el reflejo o el cambio de tipo ( golang.org/doc/effective_go.html#type_switch ) dentro foo.
Cassiohg

2

Convierte interface{}en cualquier tipo.

Sintaxis:

result := interface.(datatype)

Ejemplo:

var employee interface{} = []string{"Jhon", "Arya"}
result := employee.([]string)   //result type is []string.

2
¿Estás seguro de eso? No creo que esto sea válido. Lo vas a ver: invalid type assertion: potato.([]string) (non-interface type []interface {} on left)
Adriano Tadao

2

En caso de que necesite acortar más su código, puede crear un nuevo tipo para ayudante

type Strings []string

func (ss Strings) ToInterfaceSlice() []interface{} {
    iface := make([]interface{}, len(ss))
    for i := range ss {
        iface[i] = ss[i]
    }
    return iface
}

luego

a := []strings{"a", "b", "c", "d"}
sliceIFace := Strings(a).ToInterfaceSlice()
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.