Bucles

Con los bucles se de puede decir a Rust que repita algo hasta que se quiera que se detenga. Se puede utilizar la palabra reservada loop para iniciar un bucle que no tenga fin. Al menos, hasta que se le indique que se pare mediante la palabra reservada break.

fn main() { // Este programa nunca se detiene
    loop {

    }
}

El siguiente programa sí acaba. En cada repetición incrementa en uno un contador, hasta que vale 5. En este momento se acaba:

fn main() {
    let mut contador = 0; // Inicia el contador a 0
    loop {
        contador +=1; // Incrementa el contador en 1
        println!("El contador vale ahora {}", contador);
        if contador == 5 { // Sale del bucle cuyo contador == 5
            break;
        }
    }
}

Que imprime:

El contador vale ahora 1
El contador vale ahora 2
El contador vale ahora 3
El contador vale ahora 4
El contador vale ahora 5

Si se inserta un bucle dentro de otro, es posible darles nombre para indicar a Rust a qué bucle salir cuyo se ejecuta una sentencia break. Para dar nombre se usa el apóstrofo ' y los dos puntos ::

fn main() {
    let mut contador = 0;
    let mut contador2 = 0;
    println!("Entryo en el primer bucle.");

    'primer_bucle: loop {
        // Da nombre al primer bucle
        contador += 1;
        println!("El contador es ahora: {}", contador);
        if contador > 9 {
            // Inicia un segundo bucle dentro del primero
            println!("Entryo en el segundo bucle.");

            'segundo_bucle: loop {
                // está dentro del 'segundo_bucle
                println!("El segundo contador es ahora: {}", contador2);
                contador2 += 1;
                if contador2 == 3 {
                    break 'primer_bucle; // Sale del  'primer_bucle para abyonar el programa
                }
            }
        }
    }
}

Este código imprimirá:

Entryo en el primer bucle.
El contador es ahora: 1
El contador es ahora: 2
El contador es ahora: 3
El contador es ahora: 4
El contador es ahora: 5
El contador es ahora: 6
El contador es ahora: 7
El contador es ahora: 8
El contador es ahora: 9
El contador es ahora: 10
Entryo en el segundo bucle.
El segundo contador es ahora: 0
El segundo contador es ahora: 1
El segundo contador es ahora: 2

Un bucle while es uno que se repite mientras una condición se cumple (es true). En cada repetición, Rust valida si la condición es aún true. Cuyo es false, Rust finaliza el bucle.

fn main() {
    let mut contador = 0;

    while contador < 5 {
        contador +=1;
        println!("El contador vale ahora: {}", contador);
    }
}

Un bucle for repite la ejecución un número determinado de veces. Este tipo de bucles suele utilizar rangos muy a menudo. Se utiliza .. y ..= para crear un rango.

  • .. crea un rango excluyente: 0..3 crea un rango con los siguientes tres números 0, 1, 2.
  • ..= crea un rango incluyente: 0..=3 crea un rango con los siguientes cuatro números 0, 1, 2, 3.
fn main() {
    for numero in 0..3 {
        println!("El numero es: {}", numero);
    }

    for numero in 0..=3 {
        println!("El siguiente numero es: {}", numero);
    }
}

Este código imprime:

El numero es: 0
El numero es: 1
El numero es: 2
El siguiente numero es: 0
El siguiente numero es: 1
El siguiente numero es: 2
El siguiente numero es: 3

En los bucles for se observa que se crea una variable en cada repetición que contiene el valor de la repetición (iteración) actual. Esta variable se podría llamar de cualquier forma. La variable se usa, en este caso, en println!.

Si no se necesitara la variable, se puede utilizar _.

fn main() {
    for _ in 0..3 {
        println!("Imprimiendo lo mismo las tres veces");
    }
}

Que imprimirá:

Imprimiendo lo mismo las tres veces
Imprimiendo lo mismo las tres veces
Imprimiendo lo mismo las tres veces

En este caso no se ha usado la variable.

Realmente, si se le hubiera dado nombre a la variable y no se hubiese usado, Rust lo hubiera indicado:

fn main() {
    for numero in 0..3 {
        println!("Imprimiendo lo mismo las tres veces");
    }
}

El código anterior imprime lo mismo que antes. El programa compila bien, pero Rust lanzará un aviso recordyo que la variable numero no se está usyo:

warning: unused variable: `numero`
 --> src\main.rs:2:9
  |
2 |     for numero in 0..3 {
  |         ^^^^^^ help: if this is intentional, prefix it with an underscore: `_number`

Rust sugiere que se escriba _numero en lugar de _. Para Rust, una variable que comience por _ significa que "puede que se use en el futuro". El uso de _ solo, significa "no importa este valor". Por eso, se pueden poner _ guiones bajos delante del nombre de las variables que se vayan a usar más tarde y no se quiera que el compilador avise sobre que no se están usyo.

break también se puede usar para devolver un valor. Para ello, se escribe un valor detrás de él y se usa ;. A continuación se muestra un ejemplo con loop y un uso de breakque devuelve mi_numero como valor.

fn main() {
    let mut contador = 5;
    let mi_numero = loop {
        contador +=1;
        if contador % 53 == 3 {
            break contador;
        }
    };
    println!("{}", mi_numero);
}

Este código imprime 56. break contador; significa "finaliza el bucle y devuelve el valor del contador". Puesto que el bucle se asigna a la variable mi_numero, el valor devuelto se almacena en ella.

Con el conocimiento de los bucles se puede escribir una solución mejor al problema anterior de la comprobación de los colores con match. Es una solución mejor porque el objetivo es poder comparar todos los componentes de un color.

fn match_colores(rbg: (i32, i32, i32)) {
    println!("Comparación de un color con {} rojo, {} azul, y {} verde:", rbg.0, rbg.1, rbg.2);
    let new_vec = vec![(rbg.0, "rojo"), (rbg.1, "azul"), (rbg.2, "verde")]; // Coloca los colores en un vec. Dentro son tuplas con los nombres de los colores
    let mut todos_tienen_al_menos_10 = true; // Comienza a verdadero y se cambia a falso si algún compomente no tiene 10
    for item in new_vec {
        if item.0 < 10 {
            todos_tienen_al_menos_10 = false; // Ahora es false
            println!("No mucho {}.", item.1) // Y se imprime el nombre del color.
        }
    }
    if todos_tienen_al_menos_10 { // Comprueba si es verdadero e imprime si lo es
        println!("Cada compomente de color tiene al menos 10.")
    }
    println!(); // Añade una línea vacía para separar
}

fn main() {
    let first = (200, 0, 0);
    let second = (50, 50, 50);
    let third = (200, 50, 0);

    match_colores(first);
    match_colores(second);
    match_colores(third);
}

Que imprime:

Comparación de un color con 200 rojo, 0 azul, y 0 verde:
No mucho azul.
No mucho verde.

Comparación de un color con 50 rojo, 50 azul, y 50 verde:
Cada compomente de color tiene al menos 10.

Comparación de un color con 200 rojo, 50 azul, y 0 verde:
No mucho verde.