match es la expresión de control de flujo más potente de Rust. A diferencia del switch de C o Java, Rust exige que cubras todos los casos posibles. Si te olvidas de uno, el programa no compila. Esa propiedad de exhaustividad elimina bugs enteros antes de que lleguen a producción.
Sintaxis básica
let numero = 3;
let descripcion = match numero {
1 => "uno",
2 => "dos",
3 => "tres",
_ => "otro", // _ captura cualquier valor no cubierto
};
println!("{}", descripcion);
match es una expresión: devuelve el valor del brazo que coincide. Cada brazo tiene la forma patrón => expresión.
Exhaustividad obligatoria
enum Color { Rojo, Verde, Azul }
fn describir(c: Color) -> &'static str {
match c {
Color::Rojo => "rojo",
Color::Verde => "verde",
// ERROR: Azul no está cubierto
}
}
error[E0004]: non-exhaustive patterns: `Color::Azul` not covered
--> src/main.rs:5:11
|
5 | match c {
| ^ pattern `Color::Azul` not covered
Cuando añades una nueva variante a un enum, todos los match del código de producción fallan al compilar hasta que los actualizas. Es una refactorización asistida por el compilador.
Múltiples patrones con |
let x = 5;
match x {
1 | 2 => println!("uno o dos"),
3 | 4 | 5 => println!("tres, cuatro o cinco"),
_ => println!("otro"),
}
Rangos con ..=
let nota = 7;
match nota {
0..=4 => println!("Suspenso"),
5..=6 => println!("Aprobado"),
7..=8 => println!("Notable"),
9..=10 => println!("Sobresaliente"),
_ => println!("Nota inválida"),
}
Enums con datos: desestructuración
enum Mensaje {
Mover { x: i32, y: i32 },
Escribir(String),
CambiarColor(u8, u8, u8),
Salir,
}
fn procesar(m: Mensaje) {
match m {
Mensaje::Mover { x, y } => println!("Mover ({}, {})", x, y),
Mensaje::Escribir(texto) => println!("Texto: {}", texto),
Mensaje::CambiarColor(r, g, b) => println!("Color #{:02X}{:02X}{:02X}", r, g, b),
Mensaje::Salir => println!("Salir"),
}
}
Guards con if
Un match guard añade una condición booleana adicional al patrón:
let par = (3, -2);
match par {
(x, y) if x > 0 && y > 0 => println!("Cuadrante I"),
(x, y) if x < 0 && y > 0 => println!("Cuadrante II"),
(x, _) if x == 0 => println!("En el eje Y"),
_ => println!("Otro"),
}
Bindear el valor con @
let numero = 7;
match numero {
n @ 1..=9 => println!("Un dígito: {}", n),
n @ 10..=99 => println!("Dos dígitos: {}", n),
n => println!("Tres o más: {}", n),
}
@ captura el valor que coincide con el patrón para usarlo en el brazo.
match con Option y Result
fn leer_numero(s: &str) -> Option<i32> {
s.parse().ok()
}
fn main() {
match leer_numero("42") {
Some(n) if n > 0 => println!("Positivo: {}", n),
Some(n) => println!("No positivo: {}", n),
None => println!("No es un número"),
}
}
Resumen
matches exhaustivo: cubrir todos los casos es obligatorio.- Soporta literales, rangos, enums, tuplas, structs y desestructuración.
- Múltiples patrones con
|, condiciones adicionales con guardsif. - Captura valores con
@. - Es una expresión: puede usarse en el lado derecho de una asignación.
El siguiente artículo cubre if let y while let: cómo usar pattern matching cuando solo te importa un caso y no necesitas la exhaustividad de un match completo.
