Ir al contenido

If Let y While Let en Rust

Ya usaste match para manejar Option y Result. Sin embargo, la mayoría de las veces solo te interesa un patrón y quieres ignorar todo lo demás. Escribir un match completo con una rama _ => {} que no hace nada funciona, pero es más ceremonia de la que hace falta. if let es el atajo.

fn main() {
let age: Option<i32> = Some(30);
match age {
Some(value) => println!("La edad es {value}"),
_ => {}, // este caso en realidad no nos interesa
}
}

Ese _ => {} no hace nada, salvo cumplir la regla de match de que “hay que manejar todos los casos”. Si lo único que quieres es actuar cuando hay un Some, es más código del que la idea merece.

fn main() {
let age: Option<i32> = Some(30);
if let Some(value) = age {
println!("La edad es {value}");
}
}

Salida:

La edad es 30
▶ Pruébalo tú mismo

Léelo así: “si age encaja con el patrón Some(value), ejecuta este bloque, con value valiendo lo que había dentro”. Si age es None, el bloque simplemente se salta: sin errores y sin imprimir nada.

if let puede llevar un else, igual que un if normal:

fn main() {
let age: Option<i32> = None;
if let Some(value) = age {
println!("La edad es {value}");
} else {
println!("No se indicó la edad");
}
}
▶ Pruébalo tú mismo

Funciona igual con Result, que conociste en la página Result:

fn main() {
let input = "42";
if let Ok(number) = input.parse::<i32>() {
println!("Convertido: {number}");
} else {
println!("No se pudo convertir");
}
}
▶ Pruébalo tú mismo

Igual que if tiene else if, después de un if let puede venir un else if let. Rust prueba cada patrón por orden y ejecuta el primer bloque que encaje. Es muy útil cuando tienes varios sitios donde buscar un valor, en orden de preferencia:

fn main() {
let nickname: Option<&str> = None;
let full_name: Option<&str> = Some("Ferris Crab");
if let Some(nick) = nickname {
println!("¡Hola, {nick}!");
} else if let Some(name) = full_name {
println!("Hola, {name}.");
} else {
println!("Hola, desconocido.");
}
}

Salida:

Hola, Ferris Crab.
▶ Pruébalo tú mismo

nickname es None, así que el primer bloque se salta. full_name encaja con Some(name), así que se ejecuta ese bloque, y el else final nunca se alcanza. También puedes meter una condición normal, como else if is_admin { ... }, en cualquier punto de la cadena.

Cada variable solo existe dentro de su propio bloque: nick no se puede usar en el segundo bloque, y name no se puede usar cuando termina la cadena. Si necesitas el valor desempaquetado para el resto de tu código, mira let-else en la página de Coincidencia de patrones.

while let aplica la misma idea a un bucle: sigue repitiendo mientras un valor encaje con un patrón. Es especialmente práctico para ir vaciando una colección:

fn main() {
let mut stack = vec![1, 2, 3];
while let Some(top) = stack.pop() {
println!("{top}");
}
}

Salida:

3
2
1
▶ Pruébalo tú mismo

.pop() devuelve Some(valor) mientras quede algo, y None cuando el vector está vacío; en ese momento, while let detiene el bucle automáticamente. No hace falta comprobar la longitud a mano.

Usa match cuando de verdad necesites manejar varios casos de forma distinta. Recurre a if let cuando solo te importe un patrón y todo lo demás deba ignorarse. No sustituye a match: es solo una forma más corta de escribir el caso habitual de “solo me importa una rama”.

Empieza con let mut numbers = vec![4, 8, 15]; y un total acumulado de 0. Usa while let con .pop() para sacar los números del final de uno en uno, súmalos a total e imprime el número y el nuevo total. Pista: total necesita mut, y total += n le suma un valor.

Ver solución
fn main() {
let mut numbers = vec![4, 8, 15];
let mut total = 0;
while let Some(n) = numbers.pop() {
total += n;
println!("saqué {n}, el total ahora es {total}");
}
}

Salida:

saqué 15, el total ahora es 15
saqué 8, el total ahora es 23
saqué 4, el total ahora es 27
▶ Pruébalo tú mismo

.pop() saca del final, por eso el 15 sale primero.

Guarda let input = "17";. Si se puede convertir a u32, imprime si esa edad es de un adulto (18 o más) o de un menor. Si no se puede convertir, imprime que no es un número. Prueba también a cambiar input por "abc". Pista: pon un if / else normal dentro del bloque if let.

Ver solución
fn main() {
let input = "17";
if let Ok(age) = input.parse::<u32>() {
if age >= 18 {
println!("{age}: es adulto");
} else {
println!("{age}: es menor de edad");
}
} else {
println!("'{input}' no es un número");
}
}

Salida:

17: es menor de edad
▶ Pruébalo tú mismo

Con input = "abc", la conversión falla, se ejecuta el else de fuera y se imprime 'abc' no es un número.

  • if let Patrón = valor { ... } ejecuta el bloque solo si valor encaja con Patrón; todo lo demás se salta sin avisar.
  • Añade else para manejar el caso que no encaja, igual que en un if normal, y encadena varios patrones con else if let.
  • while let Patrón = valor { ... } repite el bucle mientras el valor siga encajando; es útil para vaciar una colección con .pop().
  • Usa if let/while let cuando solo te importa un patrón; usa match cuando necesitas manejar varios casos por separado.

Comprueba lo aprendido

1. ¿Qué pasa cuando se ejecuta if let Some(value) = age y age es None?

2. ¿Cuándo conviene usar match en lugar de if let?

3. En while let Some(top) = stack.pop() { ... }, ¿cuándo se detiene el bucle?

Puntuación: 0 / 3