if let

Para algunos casos de uso, al hacer coincidir enumeraciones, match es incómodo. Por ejemplo:


#![allow(unused)]
fn main() {
// Haz `opcional` de tipo `Option<i32>`
let opcional = Some(7);

match opcional {
    Some(i) => {
        println!("Esta es una cadena muy larga y `{:?}`", i);
        // ^ Necesitamos 2 sangrías solo para poder desestructurar
        // `i` de la opción.
    },
    _ => {},
    // ^ Obligatorio porque `match` es exhaustivo. No parece
    // como espacio desperdiciado?
};

}

if let es más limpio para este caso de uso y además permite especificar varias opciones de falla:

fn main() {
    // Todos tienen el tipo `Option<i32>`
    let numero = Some(7);
    let letra: Option<i32> = None;
    let emoticon: Option<i32> = None;

    // La construcción `if let` dice: if `let` desestructura `numero` en
    // `Some(i)`, evalúa el bloque (`{}`).
    // The `if let` construct reads: "if `let` destructures `number` into
    if let Some(i) = numero {
        println!("Coincidencia {:?}!", i);
    }

    // Si necesitas especificar una falla, usa un else:
    if let Some(i) = letra {
        println!("Coincidencia {:?}!", i);
    } else {
        // La desestructuración falló. Cambia al caso de falla.
        println!("No coincide con un número. ¡Vamos con una letra!");
    }

    // Proporciona una condición de falla alterada.
    let me_gustan_las_letras = false;

    if let Some(i) = emoticon {
        println!("Coincidencia {:?}!", i);
    // Falló la desestructuración. Evalúa una condición `else if` para ver si
    // se debe tomar una rama de falla alternativa:
    } else if me_gustan_las_letras {
        println!("No coincide con un número. ¡Vamos con una letra!");
    } else {
        // La condición evaluada como falsa. Esta rama es la predeterminada:
        println!("No me gustan las letras. ¡Vamos con un emoticón :)!");
    }
}

De la misma manera, if let puede usarse para coincidir con cualquier valor de enumeración:

// Our example enum
enum Foo {
    Bar,
    Baz,
    Qux(u32)
}

fn main() {
    // Crea variables ejemplo
    let a = Foo::Bar;
    let b = Foo::Baz;
    let c = Foo::Qux(100);
    
    // La variable a coincide con Foo::Bar
    if let Foo::Bar = a {
        println!("a es foobar");
    }
    
    // La variable b no coincide con Foo::Bar
    // Entonces esto no imprimirá nada
    if let Foo::Bar = b {
        println!("b es foobar");
    }
    
    // La variable c coincide con Foo::Qux que tiene un valor
    // Similar a Some() en el ejemplo anterior
    if let Foo::Qux(valor) = c {
        println!("c es {}", valor);
    }

    // El enlace también funciona con `if let`
    if let Foo::Qux(valor @ 100) = c {
        println!("c es cien");
    }
}

Otro beneficio es que if let nos permite hacer coincidir variantes de enumeración no parametrizadas. Esto es cierto incluso en los casos en que la enumeración no implementa o deriva PartialEq. En tales casos, if Foo::Bar == a fallaría en la compilación, porque las instancias de la enumeración no se pueden equiparar, sin embargo, if let continuará funcionando.

¿Quieres un desafío? Corrige el siguiente ejemplo para usar if let:

// This enum purposely neither implements nor derives PartialEq.
// That is why comparing Foo::Bar == a fails below.
enum Foo {Bar}

fn main() {
    let a = Foo::Bar;

    // La variable a coincide con Foo::Bar
    if Foo::Bar == a {
    // ^-- esto provoca un error en tiempo de compilación. Utiliza `if let` en su lugar.
        println!("a es foobar");
    }
}

Ve también:

enum y el RFC