3 - Control de flujo en Rust - tomando decisiones

3 - Control de flujo en Rust - tomando decisiones

¡Bienvenidos de nuevo a Rustaceo! Continuamos nuestra aventura en Rust utilizando ejemplos del mundo Pokémon. En esta ocasión, exploraremos el control de flujo en Rust, una parte esencial para tomar decisiones en nuestros programas y controlar el flujo de ejecución. Acompáñame mientras descubrimos cómo las estructuras de control pueden ayudarnos a construir programas más inteligentes y dinámicos.

Introducción al control de flujo #

El control de flujo se refiere a las estructuras que permiten dirigir la ejecución de un programa basado en condiciones y repeticiones. En Rust, las principales estructuras de control son:

  • if / else
  • loop
  • while
  • for
  • match

Veamos cómo funcionan cada una de ellas utilizando ejemplos relacionados con Pokémon.

La declaración if / else #

La declaración if permite ejecutar código basado en una condición booleana.

Ejemplo: Determinar la Efectividad de un Ataque

let tipo_ataque = "Eléctrico";
let tipo_elemento = "Agua";

if tipo_ataque == "Eléctrico" && tipo_elemento == "Agua" {
    println!("¡Es súper efectivo!");
} else if tipo_ataque == "Eléctrico" && tipo_elemento == "Planta" {
    println!("No es muy efectivo...");
} else {
    println!("El ataque tiene efectividad normal.");
}

En este ejemplo:

  • Verificamos el tipo de ataque y el tipo del Pokémon defensor.
  • Imprimimos mensajes basados en la efectividad del ataque.

Uso de if en Asignaciones

Podemos usar if en asignaciones porque en Rust, if es una expresión que retorna un valor.

let nivel_item1 = 25;
let evolucion = if nivel_item1 >= 36 {
    "Raichu"
} else {
    "Item1"
};

println!("Tu Pokémon es: {}", evolucion);

Bucles loop, while y for #

Bucle loop #

El bucle loop ejecuta un bloque de código indefinidamente hasta que se encuentra una declaración break.

Ejemplo: Capturar un Pokémon hasta que sea exitoso

let mut intentos = 0;

loop {
    intentos += 1;
    println!("Intento número {} para capturar al Pokémon.", intentos);

    // Simulamos una probabilidad de captura
    let captura_exitosa = intentos == 3;

    if captura_exitosa {
        println!("¡Has capturado al Pokémon en el intento {}!", intentos);
        break;
    }
}

Bucle while #

El bucle while ejecuta un bloque de código mientras una condición sea verdadera.

Ejemplo: Entrenar a un Pokémon hasta que alcance cierto nivel

let mut nivel = 5;

while nivel < 15 {
    println!("Entrenando... Nivel actual: {}", nivel);
    nivel += 1;
}

println!("¡Tu Pokémon ha alcanzado el nivel {}!", nivel);

Bucle for #

El bucle for itera sobre una colección o un rango.

Ejemplo: Recorriendo una Lista de Pokémon

let equipo = ["Item1", "Charizard", "Blastoise", "Venusaur"];

for pokemon in equipo.iter() {
    println!("¡Vamos, {}!", pokemon);
}

Iterando sobre un Rango

for numero in 1..=6 {
    println!("Poké Ball número {}", numero);
}
  • 1..=6 incluye el número 6 (rango inclusivo).
  • 1..6 excluye el número 6 (rango exclusivo).

La declaración match #

La declaración match permite comparar un valor contra múltiples patrones, ejecutando código basado en el primer patrón coincidente.

Ejemplo: Determinar el Tipo de Pokémon

let pokemon = "Item4";

let tipo = match pokemon {
    "Item4" | "Oddish" | "Bellsprout" => "Planta",
    "Item2" | "Vulpix" | "Growlithe" => "Fuego",
    "Item3" | "Psyduck" | "Poliwag" => "Agua",
    _ => "Desconocido",
};

println!("El tipo de {} es {}.", pokemon, tipo);

En este ejemplo:

  • Usamos patrones para coincidir con múltiples valores.
  • El carácter _ es un comodín que coincide con cualquier valor no especificado.

Desestructuración con match

Podemos usar match para desestructurar estructuras y enumeraciones.

enum Estado {
    Salud(u8),
    Envenenado,
    Paralizado,
}

let estado_item1 = Estado::Salud(80);

match estado_item1 {
    Estado::Salud(ps) => println!("Item1 tiene {} puntos de salud.", ps),
    Estado::Envenenado => println!("Item1 está envenenado."),
    Estado::Paralizado => println!("Item1 está paralizado."),
}

Uso de if let y while let #

Estas construcciones simplifican patrones comunes al trabajar con opciones o enumeraciones.

Ejemplo con if let

let captura = Some("Item1");

if let Some(pokemon) = captura {
    println!("¡Has capturado a {}!", pokemon);
} else {
    println!("No has capturado ningún Pokémon.");
}

Ejemplo con while let

let mut pokebolas = vec!["Poké Ball", "Super Ball", "Ultra Ball"];

while let Some(pokebola) = pokebolas.pop() {
    println!("Usaste una {}.", pokebola);
}

Control de flujo en funciones #

Podemos utilizar el control de flujo para manejar retornos tempranos o errores.

Ejemplo: Función para Curar a un Pokémon

fn curar_elemento(salud_actual: i32, salud_maxima: i32) -> i32 {
    if salud_actual >= salud_maxima {
        println!("El Pokémon ya tiene su salud máxima.");
        return salud_actual;
    }

    let nueva_salud = salud_maxima;
    println!("El Pokémon ha sido curado a {} puntos de salud.", nueva_salud);
    nueva_salud
}

let salud_item1 = curar_elemento(50, 100);

Práctica: elegir el movimiento correcto en batalla #

Utilizando lo que hemos aprendido, vamos a crear un programa que seleccione el mejor movimiento para un Pokémon basado en el tipo del oponente.

fn main() {
    let tipo_oponente = "Agua";

    let movimiento = match tipo_oponente {
        "Fuego" => "Lluvia de Hojas",
        "Agua" => "Impactrueno",
        "Planta" => "Ascuas",
        _ => "Ataque Rápido",
    };

    println!("El mejor movimiento contra un Pokémon de tipo {} es {}.", tipo_oponente, movimiento);
}

Conclusión #

El control de flujo es esencial para crear programas que puedan tomar decisiones y reaccionar a diferentes situaciones. Rust proporciona herramientas potentes y expresivas para manejar el flujo de ejecución de manera segura y eficiente.

Al utilizar ejemplos del mundo Pokémon, hemos visto cómo aplicar estructuras de control en situaciones familiares. Te animo a que experimentes con estos conceptos y crees tus propios programas que tomen decisiones basadas en diferentes condiciones.


¿Te ha gustado este artículo? ¡Intenta implementar tus propias estructuras de control para manejar situaciones complejas en tus programas de Rust y Pokémon! Si tienes preguntas o deseas compartir tus experiencias, ¡deja un comentario abajo!

Próxima vez en Rustaceo: Nos sumergiremos en “Propiedad y Préstamos en Rust: Gestionando la Memoria de Forma Segura”, continuando nuestra aventura en el fascinante mundo de Rust.

¡Hasta la próxima, entrenadores!