Lección 2 · 2 de 20
Interfaz vacía y switch de tipos · Go
Guarde valores de cualquier tipo con any (interface{}) en Go y recupere el tipo concreto con aserciones de tipo y switch de tipos.

La interfaz vacía interface{} no pide ningún método, así que todos los tipos la cumplen: una variable de ese tipo puede guardar cualquier valor. Desde Go 1.18 tiene un nombre más corto, `any`. values es un []any con un entero, un decimal, un texto, una lista, un booleano y nil, como los ajustes que llegan de un fichero de configuración.
Un any guarda el valor y su tipo, pero para trabajar con él hay que recuperar el tipo concreto. El switch de tipos switch v := v.(type) lo hace: dentro de cada case, v ya es de ese tipo, y por eso en case string vale len(v) y en case []string se pueden contar los elementos. case nil atrapa el valor ausente y default, todo lo demás; %T escribe el nombre del tipo (bool).
Para un solo tipo está la aserción de tipo. Con dos resultados, n, ok := port.(int), no hay riesgo: port guarda el texto "8080", no un entero, así que ok vale false y n se queda en el valor cero, 0.
Con un solo resultado, values[0].(int) devuelve el entero directamente (8080 * 2 = 16160), pero si el tipo no coincidiera provocaría un panic y el programa se detendría. Úsela solo cuando esté seguro del tipo; si hay dudas, use ok o un switch.
any es útil en los bordes del programa, como un JSON sin estructura fija o el propio fmt.Println, pero con él se pierde la comprobación de tipos del compilador. Dentro de su código, prefiera tipos concretos, interfaces con métodos o genéricos.
Código · main.go
package mainimport "fmt"// describe uses a type switch: in each case, v already has that concrete type.func describe(v interface{}) string { switch v := v.(type) { case nil: return "nil: the setting is missing" case int: return fmt.Sprintf("int %d", v) case float64: return fmt.Sprintf("float64 %.2f", v) case string: return fmt.Sprintf("string %q (%d bytes)", v, len(v)) case []string: return fmt.Sprintf("[]string with %d items: %v", len(v), v) default: return fmt.Sprintf("something else: %T", v) }}func main() { // any is another name for interface{}: it can hold a value of any type. values := []any{8080, 0.75, "shop.local", []string{"api", "eu"}, true, nil} for _, v := range values { fmt.Println(describe(v)) } var port any = "8080" n, ok := port.(int) // comma-ok form: never panics fmt.Println("port as int:", n, ok) fmt.Println("first value doubled:", values[0].(int)*2) // panics if wrong}Salida · go run main.go
int 8080 float64 0.75 string "shop.local" (10 bytes) []string with 2 items: [api eu] something else: bool nil: the setting is missing port as int: 0 false first value doubled: 16160
Para recordar
anyes lo mismo queinterface{}: admite cualquier valor.switch v := x.(type)da avel tipo de cadacase.n, ok := x.(int)nunca falla:okdice si el tipo coincide.x.(int)sinokprovoca un panic si el tipo no es el esperado.- Use
anyen los bordes; dentro, tipos concretos.
Ejercicio
Añada a describe un case bool que escriba flag on o flag off, y cambie la última línea por values[2].(int) para ver el panic que produce una aserción equivocada.