rust-bucles-loop-while-for

Bucles en Rust: cómo usar loop, while, for y rangos

  • 5 min

Un bucle es una estructura de control que repite código mientras se cumple una condición o quedan elementos por recorrer.

Si el control de flujo (if/else) es el cerebro que toma decisiones, los bucles son el músculo que realiza el trabajo pesado. Repetir una tarea miles de veces es para lo que se inventaron los ordenadores.

Rust nos ofrece tres tipos de bucles para manejar la repetición: loop, while y for.

Si vienes de C o Java, te sentirás cómodo con while y for, pero loop te parecerá extraño. Además, el for de Rust no es el típico “inicializo; compruebo; incremento” de C, sino un iterador moderno mucho más seguro.

El incombustible: loop

La palabra clave loop crea un bucle infinito. Sencillamente, ejecuta el bloque de código una y otra vez hasta que nosotros lo detengamos explícitamente o el programa se cierre.

fn main() {
    loop {
        println!("¡Esto no para nunca!");
        // Si ejecutaras esto, tendrías que pulsar Ctrl+C para matarlo
    }
}
Copied!

¿Para qué sirve un bucle que no acaba? Es muy útil en situaciones donde no sabemos cuántas veces tenemos que iterar, como en:

  • Un servidor web escuchando peticiones.
  • El “Game Loop” de un videojuego.
  • Un hilo que procesa mensajes en segundo plano.

Controlar el bucle con break y continue

Para salir de un bucle infinito (o de cualquiera de los otros tipos), usamos:

  • break: Rompe el bucle inmediatamente y sale.
  • continue: Salta el resto del código de la iteración actual y vuelve al principio del bucle para la siguiente vuelta.
fn main() {
    let mut contador = 0;

    loop {
        contador += 1;

        if contador == 3 {
            println!("Saltamos el 3");
            continue; // Vuelve arriba, no imprime el resto
        }

        println!("Contador: {}", contador);

        if contador == 5 {
            println!("¡Basta!");
            break; // Salimos del bucle
        }
    }
}
Copied!

loop como expresión

Esto es algo bastante específico de Rust. Como loop es una expresión, podemos usarlo para calcular un valor y devolverlo mediante break.

Imagina un bucle que busca un número y, cuando lo encuentra, lo asigna a una variable.

fn main() {
    let mut contador = 0;

    let resultado = loop {
        contador += 1;

        if contador == 10 {
            break contador * 2; // Retornamos 20
        }
    }; // <-- Nota el punto y coma aquí, estamos asignando a 'resultado'

    println!("El resultado es: {}", resultado);
}
Copied!

Esto evita tener que declarar una variable mutable fuera del bucle y modificarla dentro.

El condicional: while

El bucle while es el clásico “mientras se cumpla la condición”. Evalúa una expresión booleana; si es true, ejecuta el código. Si es false, sale.

fn main() {
    let mut numero = 3;

    while numero != 0 {
        println!("{}!", numero);
        numero -= 1;
    }

    println!("¡DESPEGUE!");
}
Copied!

Evita usar while para iterar colecciones cuando no necesites el índice. En C++ es habitual usar while con un índice (i) para recorrer un array.

En Rust, este patrón es más propenso a errores y puede introducir comprobaciones de límites que un iterador evita o facilita optimizar. Para recorrer los elementos directamente, usamos for.

El rey de los bucles: for

El bucle for es una de las estructuras más utilizadas en Rust. No itera sobre una condición, sino sobre cualquier valor que implemente IntoIterator, como una colección o un rango. Es seguro, rápido y conciso.

Iterar sobre rangos

Si quieres ejecutar un código un número determinado de veces, usamos la sintaxis de Rango (Range).

  • a..b: Rango desde a hasta b (exclusivo, no incluye b).
  • a..=b: Rango desde a hasta b (inclusivo, incluye b).
fn main() {
    // Del 1 al 3 (el 4 no entra)
    println!("Cuenta normal:");
    for i in 1..4 {
        println!("{}", i);
    }

    // Del 1 al 4 (inclusive)
    println!("Cuenta inclusiva:");
    for i in 1..=4 {
        println!("{}", i);
    }
}
Copied!

¿Y si queremos ir hacia atrás? Los rangos en Rust no detectan automáticamente si el segundo número es menor. Debemos usar el método .rev() (reverse).

// Cuenta atrás: 4, 3, 2, 1
for i in (1..5).rev() {
    println!("{}...", i);
}
Copied!

Iterar sobre colecciones

La mejor forma de recorrer un Array es usando for. Rust se encarga de darnos cada elemento sin riesgo de salirnos de los límites de la memoria.

fn main() {
    let a = [10, 20, 30, 40, 50];

    for elemento in a {
        println!("El valor es: {}", elemento);
    }
}
Copied!

Esto es lo que llamamos una abstracción de coste cero. Los iteradores están diseñados para compilarse sin añadir sobrecoste frente a un recorrido manual equivalente, pero evitan muchos errores con los índices.

Etiquetas de bucle

A veces tenemos bucles anidados (un bucle dentro de otro) y queremos hacer un break que salga de ambos, no solo del interno.

Por defecto, break rompe el bucle más cercano. Para especificar cuál queremos romper, podemos ponerle una etiqueta (label) al bucle. Las etiquetas en Rust empiezan con una comilla simple '.

fn main() {
    let mut contador = 0;

    'bucle_externo: loop {
        println!("Entrando al bucle externo");

        'bucle_interno: loop {
            println!("Entrando al bucle interno");

            // Lógica...
            if contador == 2 {
                break 'bucle_externo; // ¡Salimos de TODO!
            }

            contador += 1;
            break; // Esto solo rompe 'bucle_interno
        }
    }

    println!("Fin del programa");
}
Copied!

Es una característica rara vez usada, pero tremendamente útil cuando la necesitas (por ejemplo, buscando un valor en una matriz bidimensional).