En Rust, el control de flujo avanzado permite manejar estructuras de datos complejas y algoritmos de búsqueda de manera eficiente mediante el uso de etiquetas (labels), la captura de valores desde bucles y una gestión precisa de los retornos.
Etiquetas y bucles anidados
Cuando trabajamos con bucles anidados (un bucle dentro de otro), el uso de break o continue normalmente solo afecta al bucle más interno. Para romper o saltar ciclos en niveles superiores, Rust utiliza etiquetas. Una etiqueta es un identificador seguido de dos puntos (ej. 'nombre:) colocado antes de la sentencia del bucle. Esto es esencial en implementaciones de parsers de estado o búsquedas en matrices multidimensionales donde, una vez encontrado un elemento, se desea salir de toda la estructura de control inmediatamente.
Break con valor
A diferencia de while o for, la sentencia loop en Rust es una expresión que puede devolver un valor. Esto se logra mediante break valor;. Si utilizas una etiqueta con break, la sintaxis es break 'etiqueta valor;. Si se hace esto de forma incorrecta (por ejemplo, intentando usar break con valor en un bucle for), el compilador arrojará un error, ya que el break con valor es una característica específica del tipo loop.
El uso de return y la diferencia con el retorno implícito
Existen dos formas de finalizar una función:
1. Retorno explícito: Usar la palabra clave return seguida de una expresión. Esto interrumpe la ejecución de la función inmediatamente.
2. Retorno implícito: Si la última línea de una función es una expresión sin punto y coma (;), ese valor se convierte automáticamente en el valor de retorno de la función. Es la forma idiomática en Rust para el flujo normal de la función.
Advertencia sobre Closures: Un error común es asumir que return dentro de un closure (clausura) saldrá de la función contenedora. En realidad, return solo sale del cuerpo del closure. Para salir de la función principal desde un closure, se requiere lógica adicional, ya que el return tiene un alcance limitado al bloque donde fue invocado.
fn main() {
let matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
let target = 5;
let result = find_in_matrix(&matrix, target);
println!("Resultado encontrado: {:?}", result);
}
/// Busca un elemento en una matriz y devuelve su valor si se encuentra.
fn find_in_matrix(matrix: &[[i32; 3]; 3], target: i32) -> Option<i32> {
// 'search_loop es la etiqueta para el bucle principal
'search_loop: loop {
let mut found = false;
for row in matrix.iter() {
for &item in row.iter() {
if item == target {
found = true;
// Rompe el loop etiquetado 'search_loop y devuelve el valor encontrado
break 'search_loop Some(item);
}
}
}
// Si el bucle for termina sin encontrar el target, rompemos el loop principal con None
if !found {
break None;
}
};
// Definición de un closure para transformar el resultado
// El return aquí solo afecta al scope del closure, no a find_in_matrix
let transform = |x: i32| -> i32 {
if x > 100 {
return 0; // Salida temprana del closure
}
x * 2
};
// Aplicamos el closure al resultado obtenido del loop
let final_value = transform(result.unwrap_or(0));
// Retorno implícito: el valor de final_value es lo que devuelve la función
if final_value == 0 {
None
} else {
Some(final_value)
}
}
Análisis del código
matrixetarget: Se definen una matriz de tipo[[i32; 3]; 3]y un enteroi32para la búsqueda.'search_loop: loop: Se define una etiqueta llamada'search_looppara el bucle infinitoloop. Esto nos permite controlar la salida desde los buclesforinternos.break 'search_loop Some(item): Cuandoitemcoincide contarget, usamos la etiqueta para romper el bucle infinito y devolver unOption<i32>con el valorSome(item).break None: Si los buclesforterminan sin éxito, usamos unbreaksin etiqueta para salir delloopprincipal devolviendoNone.transform: Es un closure que recibe uni32. Contiene unreturn 0;, el cual sale del bloque del closure si la condición se cumple, pero permite que la funciónfind_in_matrixcontinúe su ejecución.result.unwrap_or(0): Extrae el valor deresult(que es de tipoOption<i32>) o usa0si esNone.final_value: Almacena el resultado de aplicartransformal valor extraído.- Retorno implícito: Las últimas líneas de
find_in_matrixno tienen unreturnexplícito al final; simplemente presentan la expresiónif ... else ...que determina el valor de retorno de la función de forma implícita.
N° 23