intermedio⏱45 minutosLección 1 de 10
Match y Patrones
Domina la coincidencia de patrones de Rust: literales, rangos, desestructuración, guardias y ergonomía avanzada de match
Match y Patrones
La coincidencia de patrones es una de las características más poderosas de Rust. Combina lógica condicional con desestructuración en una única sintaxis expresiva.
Repaso de Sintaxis de Match
rust
fn describir_numero(n: i32) -> &'static str {
match n {
0 => "cero",
1 | 2 | 3 => "pequeño",
4..=10 => "mediano",
_ if n > 100 => "grande", // Guardia
_ => "otro",
}
}Tipos de Patrones
Patrones Literales
Coincide con valores específicos:
rust
fn es_fin_de_semana(dia: &str) -> bool {
match dia {
"sábado" | "domingo" => true,
_ => false,
}
}Patrones de Rango
rust
fn nota_por_puntuacion(puntos: u8) -> &'static str {
match puntos {
90..=100 => "A",
80..=89 => "B",
70..=79 => "C",
60..=69 => "D",
0..=59 => "F",
}
}
fn coincidir_char(c: char) -> &'static str {
match c {
'a'..='z' => "minúscula",
'A'..='Z' => "mayúscula",
'0'..='9' => "dígito",
_ => "otro",
}
}Note
Patrones de Desestructuración
Structs
rust
struct Punto {
x: i32,
y: i32,
}
fn main() {
let p = Punto { x: 10, y: 20 };
match p {
Punto { x, y } => println!("({x}, {y})"),
}
// Desestructuración parcial con ..
match p {
Punto { x, .. } => println!("x: {x}"),
}
// Renombrar campos
match p {
Punto { x: a, y: b } => println!("({a}, {b})"),
}
}Enums
rust
enum Mensaje {
Salir,
Mover { x: i32, y: i32 },
Escribir(String),
CambiarColor(i32, i32, i32),
}
fn manejar(msg: Mensaje) {
match msg {
Mensaje::Salir => println!("salir"),
Mensaje::Mover { x, y } => println!("mover a ({x}, {y})"),
Mensaje::Escribir(texto) => println!("{texto}"),
Mensaje::CambiarColor(r, g, b) => println!("RGB({r}, {g}, {b})"),
}
}Tuplas
rust
fn describir_punto(p: (i32, i32, i32)) -> &'static str {
match p {
(0, 0, 0) => "origen",
(0, 0, _) => "en el eje z",
(0, _, _) => "en el plano x=0",
(_, 0, _) => "en el plano y=0",
(_, _, 0) => "en el plano z=0",
_ => "en el espacio",
}
}Arrays y Slices
rust
fn primero_y_ultimo(arr: &[i32]) -> Option<(&i32, &i32)> {
match arr {
[primero, .., ultimo] => Some((primero, ultimo)),
_ => None,
}
}
fn suma_dos_primeros(arr: &[i32]) -> i32 {
match arr {
[a, b, ..] => a + b,
[a] => *a,
[] => 0,
}
}Success
Los patrones de slice con .. (patrón de resto) facilitan la coincidencia en la estructura de colecciones. El compilador verifica exhaustividad incluso con patrones complejos.
Patrones de Guardia
Agrega condiciones extras con if:
rust
fn clasificar_numero(n: i32) -> &'static str {
match n {
x if x < 0 => "negativo",
x if x % 2 == 0 => "positivo par",
_ => "positivo impar",
}
}
fn coincidir_par(par: (i32, i32)) -> &'static str {
match par {
(x, y) if x == y => "iguales",
(x, y) if x + y == 0 => "negativos",
(x, _) if x > 0 => "primero positivo",
_ => "otro",
}
}Enlaces @
Vincula el valor coincidente a un nombre mientras desestructuras:
rust
fn inspeccionar(valor: Option<i32>) {
match valor {
Some(x @ 0..=10) => println!("positivo pequeño: {x}"),
Some(x @ 11..=100) => println!("positivo mediano: {x}"),
Some(x) => println!("positivo grande: {x}"),
None => println!("ninguno"),
}
}
// Con desestructuración anidada
struct Persona {
nombre: String,
edad: u8,
}
fn saludar(persona: Persona) {
match persona {
Persona { nombre, edad: edad @ 0..=12 } => {
println!("Hola {nombre}, tienes {edad} — eres niño!");
}
Persona { nombre, edad: edad @ 13..=19 } => {
println!("Hey {nombre}, tienes {edad} — eres adolescente!");
}
Persona { nombre, edad } => {
println!("Hola {nombre}, tienes {edad}.");
}
}
}Múltiples Patrones con |
rust
fn es_vocal(c: char) -> bool {
matches!(c, 'a' | 'e' | 'i' | 'o' | 'u' | 'A' | 'E' | 'I' | 'O' | 'U')
}
fn describir_dia(n: u8) -> &'static str {
match n {
1 | 7 => "fin de semana",
2..=6 => "día laboral",
_ => "inválido",
}
}La Macro matches!
Para verificaciones booleanas simples de patrones:
rust
fn main() {
let foo = Some(42);
assert!(matches!(foo, Some(x) if x > 0));
let arr = [1, 2, 3];
assert!(matches!(arr, [1, _, _]));
}Ergonomía de Match
Rust desreferencia automáticamente en patrones de match:
rust
fn main() {
let x = &Some(42);
// Sin ergonomía (pre-2018):
match x {
&Some(ref y) => println!("{y}"),
&None => (),
}
// Con ergonomía (2018+):
match x {
Some(y) => println!("{y}"),
None => (),
}
}Patrones Ref
Usa ref y ref mut para tomar valores prestados:
rust
fn main() {
let s = Some(String::from("hola"));
match s {
Some(ref s) => println!("prestado: {s}"), // s es &String
None => (),
}
println!("aún poseído: {:?}", s); // s no fue movido
let mut t = Some(String::from("mundo"));
match t {
Some(ref mut s) => *s = String::from("cambiado"),
None => (),
}
println!("{:?}", t); // Some("cambiado")
}Coincidencia de Patrones en Otros Contextos
Declaraciones let
rust
let (x, y, z) = (1, 2, 3);
let Punto { x: a, y: b } = Punto { x: 10, y: 20 };Parámetros de Función
rust
fn imprimir_coordenadas(&(x, y): &(i32, i32)) {
println!("({x}, {y})");
}
fn sumar_coordenadas(Punto { x, y }: &Punto) -> i32 {
x + y
}if let / while let
rust
fn main() {
let mut pila = vec![1, 2, 3];
while let Some(tope) = pila.pop() {
println!("{tope}");
}
let coords = (0, 5);
if let (0, y) = coords {
println!("en el eje y en {y}");
}
}for loops
rust
fn main() {
let pares = [(1, 2), (3, 4), (5, 6)];
for (a, b) in &pares {
println!("{a} + {b} = {}", a + b);
}
let mapa = std::collections::HashMap::from([("a", 1), ("b", 2)]);
for (clave, valor) in &mapa {
println!("{clave}: {valor}");
}
}Coincidencia de Tipos de Referencia y Puntero
rust
fn main() {
let x = 42;
let y = &x;
let z = Box::new(x);
match y {
42 => println!("coincide con el valor (auto-deref)"),
_ => println!("sin coincidencia"),
}
match &z {
42 => println!("Box también coincide"),
_ => println!("sin coincidencia"),
}
}Mundo Real: Evaluador de Expresiones
rust
#[derive(Debug)]
enum Expr {
Lit(i32),
Add(Box<Expr>, Box<Expr>),
Sub(Box<Expr>, Box<Expr>),
Mul(Box<Expr>, Box<Expr>),
Div(Box<Expr>, Box<Expr>),
}
fn evaluar(expr: &Expr) -> Option<i32> {
match expr {
Expr::Lit(n) => Some(*n),
Expr::Add(l, r) => Some(evaluar(l)? + evaluar(r)?),
Expr::Sub(l, r) => Some(evaluar(l)? - evaluar(r)?),
Expr::Mul(l, r) => Some(evaluar(l)? * evaluar(r)?),
Expr::Div(l, r) => {
let r_val = evaluar(r)?;
if r_val == 0 {
None
} else {
Some(evaluar(l)? / r_val)
}
}
}
}
fn main() {
let expr = Expr::Add(
Box::new(Expr::Lit(10)),
Box::new(Expr::Mul(
Box::new(Expr::Lit(3)),
Box::new(Expr::Lit(5)),
)),
);
println!("{:?}", evaluar(&expr)); // Some(25)
}Preguntas de Práctica
- ¿Qué significa el patrón
..al desestructurar un struct? - ¿Cómo coincidir un rango de valores en match?
- ¿Cuál es la diferencia entre
|y..=en patrones? - ¿Cuándo usarías una guardia de match en lugar de un match anidado?
- ¿Qué hace el enlace
@? - ¿Cómo difiere la coincidencia de patrones en
if letdematch? - ¿Qué son los patrones ref y cuándo se necesitan?
- ¿Cómo simplifica la macro
matches!la coincidencia de patrones? - ¿Puedes desestructurar un slice con patrones? Da un ejemplo.
- ¿Cómo maneja Rust la desreferenciación automática en patrones de match?
Progreso10%