if let y while let son formas concisas de aplicar patrones cuando solo nos interesa una parte de los casos.
En el artículo anterior vimos la potencia de match. Su exhaustividad nos da una seguridad increíble. A veces, siendo honestos, es “matar moscas a cañonazos”.
Hay situaciones donde solo nos importa una variante de un Enum y queremos ignorar todas las demás. Escribir un bloque match con un _ => () al final funciona, pero añade mucho ruido visual (boilerplate).
Para esos casos, Rust nos ofrece dos herramientas de “azúcar sintáctico” que limpian nuestro código: if let y while let.
El problema del match excesivo
Imagina que estamos trabajando con una variable configuracion que es un Option<String>. Solo queremos imprimir un mensaje si hay un valor (Some). Si es None, no queremos hacer nada.
Con lo que sabemos hasta ahora, haríamos esto:
let configuracion = Some(String::from("Modo Oscuro"));
match configuracion {
Some(valor) => println!("Configuración cargada: {}", valor),
None => (), // Boilerplate: Tenemos que escribir esto obligatoriamente
}Es un poco verboso, ¿verdad? Y además, la indentación nos come espacio.
La solución: if let
La sintaxis if let nos permite combinar if y let para manejar valores que coincidan con un patrón y, de paso, ignorar el resto.
El código anterior se traduce a:
let configuracion = Some(String::from("Modo Oscuro"));
// Se lee: "Si el patrón 'Some(valor)' coincide con 'configuracion', ejecuta el bloque"
if let Some(valor) = configuracion {
println!("Configuración cargada: {}", valor);
}¡Mucho más limpio!
Entender la sintaxis
if let toma un patrón a la izquierda y una expresión a la derecha, separados por un signo igual =.
if let PATRÓN = EXPRESIÓN {
// Código a ejecutar si coincide
}Cuidado visual:
Aunque usa el signo =, no es una asignación normal. No estamos guardando la variable en el patrón, estamos intentando hacer Pattern Matching.
Añadir else
Al igual que un if normal, if let puede tener un bloque else. Este bloque se ejecutará si el patrón NO coincide (es decir, equivaldría al caso _ del match).
let moneda = Moneda::Euro;
if let Moneda::Euro = moneda {
println!("¡Tenemos euros!");
} else {
println!("No son euros, habrá que cambiar divisa.");
}Bucles con while let
Si if let es la versión concisa de un match de un solo uso, while let es la versión concisa de un bucle que depende de un patrón.
Es extremadamente útil cuando trabajamos con iteradores o estructuras que pueden “agotarse” (devolver None en algún momento).
Imagina que tenemos un vector y queremos ir sacando elementos (pop) hasta que se vacíe. El método pop() devuelve Option<T> (Some si hay dato, None si está vacío).
Con loop y match (verboso):
let mut pila = vec![1, 2, 3];
loop {
match pila.pop() {
Some(valor) => println!("{}", valor),
None => break, // Tenemos que gestionar la salida manualmente
}
}Con while let (conciso):
let mut pila = vec![1, 2, 3];
// Se lee: "Mientras 'pila.pop()' devuelva 'Some(valor)', ejecuta el bucle"
while let Some(valor) = pila.pop() {
println!("{}", valor);
}El bucle se ejecuta mientras el patrón coincida. En el momento en que pop() devuelve None, el patrón Some(valor) ya no encaja y el bucle termina automáticamente.
¿Cuándo usar cuál?
Es fácil caer en la tentación de usar siempre if let porque es más corto, pero perder la exhaustividad del match tiene sus riesgos.
Usa match cuando… | Usa if let cuando… |
|---|---|
| Necesites manejar múltiples variantes. | Solo te importe una variante específica. |
| Quieras que el compilador te avise si añades una nueva variante al Enum (seguridad futura). | No te importe ignorar todas las demás variantes. |
| La lógica sea compleja y ramificada. | Quieras ejecutar un código simple si algo existe. |