Tipo de conversión de porciones de interfaces
Tengo curiosidad por saber por qué Go no convierte implícitamente []T
cuándo []interface{}
se convertirá implícitamente T
a 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 build
se 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.
En Go, existe una regla general de que la sintaxis no debe ocultar operaciones complejas/costosas .
La conversión de a string
en an interface{}
se realiza en tiempo O(1). La conversión de a []string
en an interface{}
también se realiza en tiempo O(1), ya que un segmento sigue siendo un valor. Sin embargo, convertir a []string
en 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 string
hacia y desde a []byte
o 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
}
Lo que te falta es eso T
y interface{}
que tiene un valor de T
tiene representaciones diferentes en la memoria, por lo que no se puede convertir de manera trivial.
Una variable de 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 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 T
se representa en la memoria de esta manera
- puntero para escribir
T
- valor
Entonces, volviendo a su pregunta original: ¿por qué go no se convierte implícitamente []T
a []interface{}
?
[]T
La 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.