En esta página

Control de flujo

12 min lectura TextoCap. 1 — Fundamentos de Go

Control de flujo en Go: simplificado y potente

El control de flujo determina qué instrucciones se ejecutan, cuántas veces y bajo qué condiciones. Go toma decisiones de diseño deliberadas para simplificar las estructuras de control: hay un solo bucle (for), los if no tienen paréntesis, y el switch no necesita break. Menos sintaxis, menos errores.

`if` y `else`: sin paréntesis

La primera diferencia que notarás si vienes de C, Java o JavaScript es que las condiciones en Go no llevan paréntesis. Las llaves {} son obligatorias siempre — Go no permite el formato de una sola línea sin llaves:

temperatura := 25

if temperatura > 30 {
    fmt.Println("Hace calor")
} else if temperatura > 20 {
    fmt.Println("Temperatura agradable")
} else {
    fmt.Println("Hace frío")
}

La declaración de inicialización del `if`

Go permite declarar una variable dentro del if que solo existe en el scope del bloque if/else. Este patrón es muy común cuando el resultado de una operación solo importa dentro del condicional:

// n solo existe dentro del if/else
if n := calcularValor(); n > 100 {
    fmt.Println("Valor alto:", n)
} else {
    fmt.Println("Valor normal:", n)
}
// n no existe aquí

// Patrón muy común con errores
if err := hacerAlgo(); err != nil {
    fmt.Println("Error:", err)
    return
}

Este patrón mantiene el código limpio: la variable está disponible exactamente donde se necesita y no contamina el scope circundante.

`switch`: elegante y sin bugs de fallthrough

El switch de Go es más poderoso y seguro que el de C o Java:

Switch básico

día := 3  // Miércoles

switch día {
case 1:
    fmt.Println("Lunes")
case 2:
    fmt.Println("Martes")
case 3:
    fmt.Println("Miércoles")
case 4:
    fmt.Println("Jueves")
case 5:
    fmt.Println("Viernes")
default:
    fmt.Println("Fin de semana")
}

Múltiples valores por caso

switch día {
case 1, 2, 3, 4, 5:
    fmt.Println("Día hábil")
case 6, 7:
    fmt.Println("Fin de semana")
}

Switch sin expresión (como un if-else-if)

Cuando no provees una expresión de comparación, el switch evalúa condiciones booleanas en cada case:

hora := 14

switch {
case hora < 12:
    fmt.Println("Buenos días")
case hora < 18:
    fmt.Println("Buenas tardes")
default:
    fmt.Println("Buenas noches")
}

Switch con inicialización

switch os := runtime.GOOS; os {
case "darwin":
    fmt.Println("macOS")
case "linux":
    fmt.Println("Linux")
default:
    fmt.Printf("Otro: %s\n", os)
}

`fallthrough` explícito

Si necesitas el comportamiento de caída, úsalo explícitamente:

n := 3
switch n {
case 3:
    fmt.Println("Tres")
    fallthrough  // ejecuta el siguiente case también
case 2:
    fmt.Println("Dos o más")
case 1:
    fmt.Println("Uno")
}
// Imprime: Tres
//          Dos o más

`for`: el único bucle de Go

Go tomó la decisión radical de tener un único tipo de bucle: for. Pero for tiene múltiples formas que cubren todos los casos de uso.

Forma 1: Clásico (init; condition; post)

// Suma de 1 a 100
suma := 0
for i := 1; i <= 100; i++ {
    suma += i
}
fmt.Println("Suma:", suma)  // 5050

// Contar hacia atrás
for i := 10; i > 0; i-- {
    fmt.Print(i, " ")
}
fmt.Println()

// Múltiples variables
for i, j := 0, 10; i < j; i, j = i+1, j-1 {
    fmt.Printf("i=%d, j=%d\n", i, j)
}

Forma 2: Condicional (equivalente a `while`)

// Leer input hasta que sea válido
entrada := 0
for entrada < 5 {
    entrada++
    fmt.Println("Procesando:", entrada)
}

// Bucle "while true" con break interno
intentos := 0
for {
    intentos++
    if intentos >= 3 {
        break
    }
}

Forma 3: Bucle infinito

// Servidor que se ejecuta para siempre (común en goroutines)
for {
    // procesar trabajo...
    // usar break o return para salir
}

`range`: iterar sobre colecciones

range es la forma idiomática de Go para iterar sobre arrays, slices, maps, strings y canales:

// Sobre un slice: índice y valor
números := []int{10, 20, 30, 40, 50}
for i, n := range números {
    fmt.Printf("números[%d] = %d\n", i, n)
}

// Solo el índice
for i := range números {
    fmt.Println(i)
}

// Solo el valor (descartar índice con _)
for _, n := range números {
    fmt.Println(n)
}

// Sobre un map: clave y valor
capitales := map[string]string{
    "Bolivia":   "Sucre",
    "Argentina": "Buenos Aires",
    "Brasil":    "Brasilia",
}
for país, capital := range capitales {
    fmt.Printf("Capital de %s: %s\n", país, capital)
}

// Sobre un string: índice de byte y rune
for i, r := range "¡Hola!" {
    fmt.Printf("índice: %d, carácter: %c\n", i, r)
}

// Go 1.22+: range sobre entero
for i := range 5 {  // 0, 1, 2, 3, 4
    fmt.Println(i)
}

`break` y `continue`

// break: salir del bucle inmediatamente
for i := 0; i < 10; i++ {
    if i == 5 {
        break
    }
    fmt.Println(i)
}
// Imprime: 0 1 2 3 4

// continue: saltar al siguiente ciclo
for i := 0; i < 10; i++ {
    if i%2 == 0 {
        continue  // saltar números pares
    }
    fmt.Println(i)
}
// Imprime: 1 3 5 7 9

Break etiquetado: salir de bucles anidados

En Go, puedes poner una etiqueta en un bucle y usarla con break o continue para controlar bucles externos. Esto evita la necesidad de variables de control adicionales:

// Sin etiquetas: necesitas una variable extra
encontrado := false
for i := 0; i < 5 && !encontrado; i++ {
    for j := 0; j < 5; j++ {
        if i*j > 6 {
            encontrado = true
            break
        }
    }
}

// Con etiquetas: más limpio
externo:
for i := 0; i < 5; i++ {
    for j := 0; j < 5; j++ {
        if i*j > 6 {
            fmt.Printf("Encontrado en (%d, %d)\n", i, j)
            break externo  // sale de ambos bucles
        }
    }
}

// continue etiquetado
externo:
for i := 0; i < 3; i++ {
    for j := 0; j < 3; j++ {
        if j == 1 {
            continue externo  // salta al siguiente i
        }
        fmt.Printf("(%d,%d) ", i, j)
    }
}
// Imprime: (0,0) (1,0) (2,0)

Resumen de patrones idiomáticos

Patrón Código
If con inicialización if err := f(); err != nil { ... }
While for condición { ... }
Infinito for { ... }
For clásico for i := 0; i < n; i++ { ... }
Iterar slice for i, v := range slice { ... }
Iterar map for k, v := range mapa { ... }
Salir de anidados break etiqueta

Con estas estructuras de control dominadas, en la próxima lección aprenderemos las funciones de Go: múltiples valores de retorno, funciones variádicas, closures y el poderoso defer.

Go tiene un solo tipo de bucle: for
Go no tiene while ni do-while. En su lugar, for tiene tres formas: clásico (for i := 0; i < n; i++), condicional como while (for condición) y bucle infinito (for {}). Esta simplificación hace el código más uniforme y predecible.
switch en Go no necesita break
A diferencia de C, Java o JavaScript, los casos de switch en Go no caen al siguiente caso automáticamente (no hay fallthrough implícito). Si quieres ese comportamiento, usa la palabra clave fallthrough explícitamente. Esto elimina un error común en otros lenguajes.