Aprende Rust

Lección 05 de 26 Primeros pasos

Control de flujo: if, loop, while y for

  • 4 min de lectura
  • 5 ejercicios
  1. if elige un camino según una condición, que siempre es un bool. Con 22 grados, temperatura > 30 es false.
  2. loop repite su bloque sin fin hasta que lo paras con break. Aquí, al llegar a 3 intentos.
  3. while repite mientras la condición sea cierta. Cuando cuenta llega a 0, el bucle termina y... ¡despegue!
  4. for recorre un rango de uno en uno. 1..4 incluye el 1 pero no el 4: da 3 vueltas.
1/4

Hasta ahora tus programas se ejecutaban de arriba abajo, línea a línea. Con el control de flujo decides qué código se ejecuta según una condición y cuántas veces se repite.

Rust tiene if para tomar decisiones y tres tipos de bucle: loop, while y for. Y, como viste en la lección anterior, casi todo en Rust es una expresión, así que if y loop también pueden devolver un valor.

if, else if y else

fn main() {
    let temperatura = 22;

    if temperatura > 30 {
        println!("Hace calor");
    } else if temperatura > 15 {
        println!("Se está bien");
    } else {
        println!("Hace frío");
    }
}
  • La condición no lleva paréntesis. Puedes ponerlos, pero el compilador te avisará de que sobran.
  • Las llaves son obligatorias, aunque el bloque tenga una sola línea.
  • Las condiciones se comprueban en orden y solo se ejecuta el primer bloque cuya condición se cumple.

Para escribir condiciones tienes los operadores de comparación ==, !=, <, >, <= y >=, que puedes combinar con && (y), || (o) y ! (no).

La condición tiene que ser un bool

En JavaScript o Python un número o un texto pueden usarse como condición: son los valores truthy y falsy. En Rust no existen: la condición tiene que ser un bool.

fn main() {
    let mensajes = 3;

    if mensajes {
        println!("Tienes mensajes");
    }
}
error[E0308]: mismatched types
 --> src/main.rs:4:8
  |
4 |     if mensajes {
  |        ^^^^^^^^ expected `bool`, found integer

La solución es escribir la comparación que quieres hacer: if mensajes > 0. Es algo más largo, pero no deja lugar a dudas.

if como expresión

Como if es una expresión, puedes usar su resultado directamente, por ejemplo para dar valor a una variable:

fn main() {
    let edad = 15;
    let entrada = if edad >= 18 { "general" } else { "reducida" };

    println!("Entrada {entrada}");
}

Cada rama es un bloque y su valor es su última expresión, igual que en las funciones. Por eso Rust no necesita el operador ternario (condicion ? a : b) de otros lenguajes.

Las dos ramas tienen que devolver el mismo tipo, porque el compilador necesita saber el tipo de la variable sin ejecutar el programa:

fn main() {
    let lleno = true;
    let valor = if lleno { 100 } else { "vacío" };
    println!("{valor}");
}
error[E0308]: `if` and `else` have incompatible types

loop: repetir hasta que lo pares

loop repite su bloque indefinidamente, hasta que lo detienes con break:

fn main() {
    let mut intentos = 0;

    loop {
        intentos += 1;
        println!("Intento {intentos}");

        if intentos == 3 {
            break;
        }
    }
}

loop también es una expresión: el valor que pongas después de break es el que devuelve el bucle. Es muy útil para repetir algo hasta conseguir un resultado:

fn main() {
    let mut contador = 0;

    let resultado = loop {
        contador += 1;

        if contador == 10 {
            break contador * 2;
        }
    };

    println!("Resultado: {resultado}");
}

Además de break, tienes continue, que salta directamente a la siguiente vuelta del bucle sin ejecutar el resto del bloque.

while: repetir mientras se cumpla algo

while comprueba una condición antes de cada vuelta y se detiene cuando deja de cumplirse:

fn main() {
    let mut cuenta = 3;

    while cuenta > 0 {
        println!("{cuenta}...");
        cuenta -= 1;
    }

    println!("¡Despegue!");
}

Podrías conseguir lo mismo con loop, un if y un break, pero while se lee mucho mejor.

for: recorrer rangos y arrays

for es el bucle que más usarás. Recorre uno a uno los elementos de algo que se pueda recorrer, como un rango de números:

fn main() {
    for i in 1..4 {
        println!("Vuelta {i}");
    }
}

Un rango inicio..fin incluye el inicio pero no el final: 1..4 recorre 1, 2 y 3. Para incluir el final, usa ..=. Y para recorrerlo al revés, .rev():

La cuenta atrás de antes, con for, queda así:

fn main() {
    for i in (1..=3).rev() {
        println!("{i}...");
    }

    println!("¡Despegue!");
}

Los paréntesis son necesarios para que .rev() se aplique al rango entero.

Recorrer un array

for también recorre directamente los elementos de un array, sin índices:

fn main() {
    let lenguajes = ["Rust", "Go", "Zig"];

    for lenguaje in lenguajes {
        println!("Me gusta {lenguaje}");
    }
}

Podrías hacerlo con while y un índice, pero for es más corto y más seguro: no puedes equivocarte con el índice ni salirte del array.

Sin variable: Si no necesitas el valor de cada vuelta, usa _ como nombre: for _ in 0..3 repite el bloque tres veces.

Etiquetas de bucle

Con bucles anidados, break y continue afectan al bucle más interno. Para salir de uno exterior, ponle una etiqueta: un nombre que empieza por comilla simple, seguido de dos puntos.

fn main() {
    'externo: for fila in 1..=3 {
        for columna in 1..=3 {
            if fila * columna == 4 {
                println!("Encontrado: {fila} × {columna} = 4");
                break 'externo;
            }
            println!("{fila} × {columna}");
        }
    }
}

break 'externo termina los dos bucles de golpe. Sin la etiqueta, solo saldría del bucle de columna y seguiría con la siguiente fila. También puedes usar continue 'externo.

¿Qué bucle elijo?

BucleÚsalo cuando…
forRecorres un rango o un array. Es la opción por defecto
whileRepites mientras se cumpla una condición
loopRepites hasta que ocurra algo dentro del bucle o quieres devolver un valor con break

Hora de practicar

Ejercicios

0/5 resueltos

Reto 1 de 5

Completa estado_bateria con if, else if y else: imprime Batería baja si nivel es menor que 20, Batería llena si es 100 y Batería normal en cualquier otro caso.
Salida esperada
Batería baja
Batería llena
Batería normal
Batería normal
src/main.rs
$ cargo run Listo

Pulsa Ejecutar para compilar y ver aquí la salida de tu programa.

¿Algo no cuadra en este reto? Avísanos