Estructuras de Control en Rust
Las estructuras de control nos permiten dirigir el flujo de ejecución de nuestros programas. En este capítulo exploraremos condicionales, bucles y pattern matching en Rust.
Expresiones Condicionales
if Básico
fn main () {
let numero = 6 ;
if numero % 4 == 0 {
println! ( "El número es divisible por 4" );
} else if numero % 3 == 0 {
println! ( "El número es divisible por 3" );
} else if numero % 2 == 0 {
println! ( "El número es divisible por 2" );
} else {
println! ( "El número no es divisible por 4, 3, o 2" );
}
}
if como Expresión
En Rust, if es una expresión, no solo una declaración:
fn main () {
let condicion = true ;
// if como expresión
let numero = if condicion { 5 } else { 6 };
println! ( "El valor de numero es: {numero}" );
// Clasificar edad
let edad = 25 ;
let categoria = if edad < 13 {
"niño"
} else if edad < 20 {
"adolescente"
} else if edad < 65 {
"adulto"
} else {
"adulto mayor"
};
println! ( "Categoría: {categoria}" );
// Los tipos deben coincidir
// let error = if condicion { 5 } else { "seis" }; // ERROR!
}
Condiciones Complejas
fn main () {
let temperatura = 22 ;
let llueve = false ;
let es_fin_de_semana = true ;
// Operadores lógicos
if temperatura > 20 && ! llueve {
println! ( "¡Perfecto para salir!" );
}
if es_fin_de_semana || temperatura > 25 {
println! ( "Buen día para relajarse" );
}
// Expresiones más complejas
let actividad = if temperatura > 30 {
if llueve {
"quedarse en casa con aire acondicionado"
} else {
"ir a la playa"
}
} else if temperatura < 10 {
"quedarse en casa"
} else if llueve {
"ir al cine"
} else {
"dar un paseo"
};
println! ( "Actividad recomendada: {actividad}" );
}
Bucles
Rust proporciona tres tipos de bucles: loop, while, y for.
loop - Bucle Infinito
fn main () {
let mut contador = 0 ;
// Bucle infinito con break
loop {
contador += 1 ;
if contador == 3 {
continue ; // Salta a la siguiente iteración
}
println! ( "Contador: {contador}" );
if contador == 5 {
break ; // Sale del bucle
}
}
println! ( "Fin del bucle" );
}
Retornar Valores desde loop
fn main () {
let mut contador = 0 ;
// loop puede retornar un valor
let resultado = loop {
contador += 1 ;
if contador == 10 {
break contador * 2 ; // Retorna el valor
}
};
println! ( "El resultado es: {resultado}" ); // 20
}
Etiquetas de Bucle
fn main () {
let mut cuenta = 0 ;
// Etiquetas para bucles anidados
' externo : loop {
println! ( "Bucle externo: {cuenta}" );
let mut restante = 10 ;
loop {
println! ( " Bucle interno: {restante}" );
if restante == 9 {
break ; // Sale solo del bucle interno
}
if cuenta == 2 {
break ' externo ; // Sale del bucle externo
}
restante -= 1 ;
}
cuenta += 1 ;
}
println! ( "Fin de bucles anidados" );
}
while - Bucle Condicional
fn main () {
let mut numero = 3 ;
// Bucle while básico
while numero != 0 {
println! ( "{numero}!" );
numero -= 1 ;
}
println! ( "¡DESPEGUE!" );
// while con condiciones complejas
let mut x = 1 ;
while x < 100 {
x *= 2 ;
println! ( "x = {x}" );
}
// Cuidado con bucles infinitos
let mut bandera = true ;
let mut iteraciones = 0 ;
while bandera {
iteraciones += 1 ;
println! ( "Iteración: {iteraciones}" );
if iteraciones >= 5 {
bandera = false ; // Evita bucle infinito
}
}
}
for - Iteración sobre Colecciones
fn main () {
// Iterar sobre array
let array = [ 10 , 20 , 30 , 40 , 50 ];
for elemento in array {
println! ( "Elemento: {elemento}" );
}
// Iterar con índices
for (indice, valor) in array . iter () . enumerate () {
println! ( "Índice {indice}: {valor}" );
}
// Rangos
for numero in 1 .. 4 { // Excluye el 4
println! ( "Número: {numero}" ); // 1, 2, 3
}
for numero in 1 ..= 4 { // Incluye el 4
println! ( "Número inclusivo: {numero}" ); // 1, 2, 3, 4
}
// Rango inverso
for numero in ( 1 .. 4 ) . rev () {
println! ( "Número reverso: {numero}" ); // 3, 2, 1
}
}
Patrones de Iteración Comunes
fn main () {
let datos = vec! [ 1 , 2 , 3 , 4 , 5 ];
// 1. Iterar por valor (consume la colección)
for item in datos . clone () {
println! ( "Por valor: {item}" );
}
// 2. Iterar por referencia inmutable
for item in & datos {
println! ( "Por referencia: {item}" );
}
// 3. Iterar por referencia mutable
let mut datos_mut = vec! [ 1 , 2 , 3 , 4 , 5 ];
for item in &mut datos_mut {
* item *= 2 ; // Modificar cada elemento
}
println! ( "Datos modificados: {:?}" , datos_mut);
// 4. Usar collect para crear nueva colección
let cuadrados : Vec < i32 > = ( 1 .. 6 )
. map ( | x | x * x)
. collect ();
println! ( "Cuadrados: {:?}" , cuadrados);
}
Pattern Matching con match
match es una característica poderosa de Rust para pattern matching:
match Básico
fn main () {
let numero = 3 ;
match numero {
1 => println! ( "Uno" ),
2 => println! ( "Dos" ),
3 => println! ( "Tres" ),
4 => println! ( "Cuatro" ),
5 => println! ( "Cinco" ),
_ => println! ( "Otro número" ), // Comodín (catch-all)
}
}
match como Expresión
fn main () {
let numero = 6 ;
let descripcion = match numero {
1 => "uno" ,
2 => "dos" ,
3 => "tres" ,
n if n > 10 => "número grande" ,
_ => "número normal" ,
};
println! ( "El número {numero} es: {descripcion}" );
}
Patrones Múltiples
fn main () {
let valor = 4 ;
match valor {
1 | 2 => println! ( "Uno o dos" ),
3 ..= 5 => println! ( "Entre tres y cinco" ),
6 | 7 | 8 => println! ( "Seis, siete u ocho" ),
_ => println! ( "Otro valor" ),
}
}
Destructuring con match
fn main () {
// Con tuplas
let punto = ( 3 , 5 );
match punto {
( 0 , 0 ) => println! ( "Origen" ),
( 0 , y) => println! ( "En el eje Y: {y}" ),
(x, 0 ) => println! ( "En el eje X: {x}" ),
(x, y) => println! ( "Punto ({x}, {y})" ),
}
// Con arrays
let array = [ 1 , 2 , 3 ];
match array {
[a, 2 , c] => println! ( "Segundo elemento es 2: [{a}, 2, {c}]" ),
[a, b, c] => println! ( "Array: [{a}, {b}, {c}]" ),
}
}
Guards en match
fn main () {
let numero = Some ( 4 );
match numero {
Some (x) if x < 5 => println! ( "Menor que cinco: {x}" ),
Some (x) => println! ( "Mayor o igual a cinco: {x}" ),
None => println! ( "No hay valor" ),
}
// Ejemplo más complejo
let par_x_y = ( 4 , - 2 );
match par_x_y {
(x, y) if x == y => println! ( "Estos son iguales" ),
(x, y) if x + y == 0 => println! ( "Se suman cero" ),
(x, _) if x % 2 == 0 => println! ( "X es par" ),
_ => println! ( "Sin patrón especial" ),
}
}
if let - Sintaxis Concisa
Para casos donde solo te interesa un patrón específico:
fn main () {
let config_max = Some ( 3 u8 );
// Con match
match config_max {
Some (max) => println! ( "El máximo está configurado a {max}" ),
_ => (),
}
// Con if let (más conciso)
if let Some (max) = config_max {
println! ( "El máximo está configurado a {max}" );
}
// Combinado con else
let numero = Some ( 7 );
if let Some (n) = numero {
if n > 5 {
println! ( "Número grande: {n}" );
}
} else {
println! ( "No hay número" );
}
}
while let - Bucle Condicional con Pattern Matching
fn main () {
let mut pila = Vec :: new ();
pila . push ( 1 );
pila . push ( 2 );
pila . push ( 3 );
// Procesar hasta que la pila esté vacía
while let Some (tope) = pila . pop () {
println! ( "Procesando: {tope}" );
}
println! ( "Pila vacía" );
}
Ejercicios Prácticos
Ejercicio 1: Juego de Adivinanza
fn main () {
let numero_secreto = 7 ;
let mut intentos = 0 ;
let max_intentos = 5 ;
loop {
intentos += 1 ;
// Simular adivinanza (normalmente leerías input del usuario)
let adivinanza = match intentos {
1 => 3 ,
2 => 9 ,
3 => 7 ,
_ => 5 ,
};
println! ( "Intento {intentos}: {adivinanza}" );
match adivinanza {
n if n == numero_secreto => {
println! ( "¡Ganaste en {intentos} intentos!" );
break ;
},
n if n < numero_secreto => println! ( "Muy bajo" ),
n if n > numero_secreto => println! ( "Muy alto" ),
_ => unreachable! (),
}
if intentos >= max_intentos {
println! ( "Se agotaron los intentos. El número era {numero_secreto}" );
break ;
}
}
}
Ejercicio 2: Clasificador de Caracteres
fn main () {
let texto = "Hola Rust 123!" ;
let mut letras = 0 ;
let mut numeros = 0 ;
let mut espacios = 0 ;
let mut otros = 0 ;
for caracter in texto . chars () {
match caracter {
'a' ..= 'z' | 'A' ..= 'Z' => letras += 1 ,
'0' ..= '9' => numeros += 1 ,
' ' => espacios += 1 ,
_ => otros += 1 ,
}
}
println! ( "Análisis de '{texto}':" );
println! ( "Letras: {letras}" );
println! ( "Números: {numeros}" );
println! ( "Espacios: {espacios}" );
println! ( "Otros: {otros}" );
}
Ejercicio 3: Tabla de Multiplicar
fn main () {
let numero = 7 ;
let limite = 10 ;
println! ( "Tabla de multiplicar del {numero}:" );
println! ( "------------------------" );
for i in 1 ..= limite {
let resultado = numero * i;
// Formatting con match
let descripcion = match i {
1 => "primera" ,
2 => "segunda" ,
3 => "tercera" ,
n if n <= 10 => "normal" ,
_ => "alta" ,
};
println! ( "{numero} x {i:2} = {resultado:3} ({descripcion} fila)" );
}
}
Ejercicio 4: Contador de Palabras
fn main () {
let frase = "Rust es un lenguaje de programación moderno y seguro" ;
let palabras : Vec < & str > = frase . split_whitespace () . collect ();
println! ( "Frase: '{frase}'" );
println! ( "Palabras encontradas: {}" , palabras . len ());
println! ();
for (indice, palabra) in palabras . iter () . enumerate () {
let categoria = match palabra . len () {
1 ..= 3 => "corta" ,
4 ..= 6 => "media" ,
7 ..= 10 => "larga" ,
_ => "muy larga" ,
};
println! ( "Palabra {}: '{}' - {} caracteres ({})" ,
indice + 1 , palabra, palabra . len (), categoria);
}
// Estadísticas
let palabra_mas_larga = palabras . iter ()
. max_by_key ( | palabra | palabra . len ())
. unwrap ();
let palabra_mas_corta = palabras . iter ()
. min_by_key ( | palabra | palabra . len ())
. unwrap ();
println! ( " \n Estadísticas:" );
println! ( "Palabra más larga: '{palabra_mas_larga}' ({} chars)" , palabra_mas_larga . len ());
println! ( "Palabra más corta: '{palabra_mas_corta}' ({} chars)" , palabra_mas_corta . len ());
}
Puntos Clave para Recordar
if es una expresión - puede retornar valores
loop crea bucles infinitos - usa break para salir
while ejecuta mientras la condición sea verdadera
for es ideal para iterar sobre colecciones y rangos
match debe ser exhaustivo - cubrir todos los casos posibles
Usa _ como comodín para casos no manejados explícitamente
if let y while let son útiles para casos específicos
Los guards (if) añaden condiciones adicionales a los patrones
Las etiquetas de bucle ayudan con bucles anidados
Anterior
Tipos Datos
Siguiente
Ownership