El switch en Go es una estructura de control que compara una expresión contra múltiples valores. A diferencia de lenguajes como C o Java, en Go cada case se ejecuta y el flujo de control sale del switch automáticamente sin necesidad de un break. Esta decisión de diseño busca evitar el error clásico de olvidar un break y dejar que la ejecución “caiga” accidentalmente en el siguiente caso. Para lograr ese comportamiento de cascada, Go requiere el uso explícito de la palabra clave fallthrough.
Un switch puede usarse de tres formas principales: con una expresión para comparar valores, como un type switch para inspeccionar tipos de interfaces, o de forma tagless (sin expresión), donde se evalúan condiciones booleanas directamente, funcionando como un sustituto mucho más limpio y legible de las cadenas de if-else-if.
Si intentas usar un switch asumiendo que la ejecución seguirá hacia abajo tras coincidir un caso, tu lógica fallará, ya que el programa simplemente saltará al final del bloque tras completar el primer case que resulte verdadero.
Cuando tienes varios valores que deben disparar la misma lógica, puedes agruparlos en una sola línea usando comas: case 1, 2, 3:. Esto reduce la duplicación de código y hace que la intención sea inmediata.
package main
import (
"fmt"
)
// Evento representa una señal en nuestro sistema
type Evento struct {
ID int
Data any
}
func main() {
eventos := []Evento{
{ID: 1, Data: "iniciar"},
{ID: 2, Data: 42},
{ID: 3, Data: 3.14},
{ID: 4, Data: "detener"},
{ID: 5, Data: true},
}
for _, e := range eventos {
procesarEvento(e)
}
}
func procesarEvento(e Evento) {
// 1. Switch con statement inicial
// Podemos declarar una variable y evaluar su estado inmediatamente
switch estado := evaluarEstado(e.ID); estado {
case "ERROR":
fmt.Printf("[!] Error crítico en ID %d\n", e.ID)
return
case "RETRY", "START":
fmt.Printf("[*] Estado de control: %s\n", estado)
// 2. Uso de fallthrough
// Ejecutamos la lógica del siguiente caso de forma explícita
fallthrough
case "PROCESAR":
fmt.Println("[+] Iniciando ejecución de lógica...")
}
// 3. Switch con múltiples valores
switch e.ID {
case 1, 2:
fmt.Println(" -> Categoría: Inicialización")
case 3:
fmt.Println(" -> Categoría: Ajuste")
default:
fmt.Println(" -> Categoría: Desconocida")
}
// 4. Type switch
// Usamos la sintaxis v := x.(type) para obtener el valor con su tipo real
switch v := e.Data.(type) {
case string:
fmt.Printf(" -> Payload string: %s\n", v)
case int:
fmt.Printf(" -> Payload entero: %d\n", v)
case float64:
fmt.Printf(" -> Payload flotante: %f\n", v)
case bool:
fmt.Printf(" -> Payload booleano: %t\n", v)
case nil:
fmt.Println(" -> Payload es nil")
default:
fmt.Printf(" -> Payload tipo %T no soportado\n", v)
}
// 5. Tagless switch (Switch sin expresión)
// Se usa para evaluar condiciones booleanas complejas de forma limpia
switch {
case e.ID > 0:
fmt.Println(" -> El ID es válido")
case e.ID == 0:
fmt.Println(" -> El ID es cero")
default:
fmt.Println(" -> El ID es inválido")
}
fmt.Println("------------------------------------")
}
func evaluarEstado(id int) string {
if id == 0 {
return "ERROR"
}
if id <= 2 {
return "START"
}
if id >= 4 {
return "RETRY"
}
return "PROCESAR"
}
Desglose del código
En la función procesarEvento, primero vemos un switch con statement inicial. La variable estado se inicializa mediante evaluarEstado(e.ID) y se evalúa inmediatamente. Si el estado es RETRY, el uso de fallthrough obliga al runtime a ejecutar también el bloque de case "PROCESAR", permitiendo que un estado de reintento también dispare la lógica de ejecución.
Luego, el switch e.ID demuestra cómo agrupar valores. Al poner case 1, 2:, evitamos tener dos bloques de código idénticos para los IDs 1 y 2.
El type switch es quizás la parte más potente. Al usar switch v := e.Data.(type), el compilador no solo nos dice de qué tipo es e.Data, sino que asigna ese valor a la variable v con su tipo específico dentro de cada case. Esto permite que dentro de case int:, v sea tratado directamente como un entero, evitando conversiones manuales costosas o peligrosas.
Finalmente, el tagless switch (switch { ... }) actúa como un reemplazo elegante para un bloque de if-else-if. Es especialmente útil cuando las condiciones no dependen de una única variable, sino de comparaciones lógicas independientes.
El error frecuente
Un error común al venir de lenguajes como C es intentar encadenar casos para que el flujo continúe sin usar fallthrough.
// ERROR: El programador espera que se ejecuten ambos prints
switch x {
case 1:
fmt.Println("Uno")
case 2:
fmt.Println("Dos")
}
// Si x es 1, solo se imprime "Uno".
Si necesitas que un caso ejecute también la lógica del siguiente, debes ser explícito con fallthrough. Ten cuidado: fallthrough no evalúa la condición del siguiente case, simplemente ejecuta su cuerpo. Si el siguiente case tiene una condición lógica compleja en un tagless switch, el fallthrough la ignorará y ejecutará el código de todos modos.
N° 38