if let

Para alguns casos de uso, quando correspondemos enumerações, match é difícil. Por exemplo:

#![allow(unused)]
fn main() {
// Tornar `optional` do tipo `Option<i32>`
let optional = Some(7);

match optional {
    Some(i) => {
        println!("This is a really long string and `{:?}`", i);
        // ^ Precisava de 2 indentações só assim poderíamos 
        // desestruturar `i` a partir da opção.
    },
    _ => {},
    // ^ Obrigatório porque `match` é exaustiva.
    // Não parece como espaço desperdiçado?
};

}

if let é mais clara para este caso de uso e além disso permite várias opções de fracasso a serem especificadas:

fn main() {
    // Todos têm o tipo `Option<i32>`
    let number = Some(7);
    let letter: Option<i32> = None;
    let emoticon: Option<i32> = None;

    // A construção `if let` lê-se: "se `let` desestruturar `number` na"
    // `Some(i)`, avaliar o bloco (`{}`).
    if let Some(i) = number {
        println!("Matched {:?}!", i);
    }

    // Se precisarmos de especificar uma falha, usamos uma `else`:
    if let Some(i) = letter {
        println!("Matched {:?}!", i);
    } else {
        // Desestruturação falhada. Mudar para o caso de falha.
        println!("Didn't match a number. Let's go with a letter!");
    }

    // Fornecer uma condição de falha alterada.
    let i_like_letters = false;

    if let Some(i) = emoticon {
        println!("Matched {:?}!", i);
    // Desestruturação falhada. Avaliar uma condição `else if` para
    // ver se o ramo de falha alternada deveria ser considerado:
    } else if i_like_letters {
        println!("Didn't match a number. Let's go with a letter!");
    } else {
        // A condição avaliou `false`. Este ramo é o padrão:
        println!("I don't like letters. Let's go with an emoticon :)!");
    }
}

De algum modo, if let pode ser usada para corresponder apenas o valor de enumeração:

// Nossa enumeração de exemplo
enum Foo {
    Bar,
    Baz,
    Qux(u32)
}

fn main() {
    // Criar variáveis de exemplo
    let a = Foo::Bar;
    let b = Foo::Baz;
    let c = Foo::Qux(100);
    
    // Variável `a` corresponde `Foo::Bar`
    if let Foo::Bar = a {
        println!("a is foobar");
    }
    
    // Variável `b` não corresponde `Foo::Bar`
    // Então isto não imprimirá nada
    if let Foo::Bar = b {
        println!("b is foobar");
    }
    
    // Variável `c` corresponde `Foo::Qux` que tem um valor
    // Semelhante à `Some()` no exemplo anterior
    if let Foo::Qux(value) = c {
        println!("c is {}", value);
    }

    // Vínculos também funcionam com `if let`
    if let Foo::Qux(value @ 100) = c {
        println!("c is one hundred");
    }
}

Um outro beneficio é que if let permite-nos corresponder variantes de enumeração não parametrizadas. Isto é verdadeiro mesmo em casos onde a enumeração não implementa ou deriva PartialEq. Em tais casos if Foo::Bar == a falharia ao compilar, porque as instâncias da enumeração não podem ser equiparadas, no entanto if let continuará a funcionar.

Gostarias dum desafio? Corrija o seguinte exemplo para usar if let:

// Esta enumeração propositadamente não implemente nem deriva `PartialEq`.
// É por isto que a comparação abaixo `Foo::Bar == a` falha.
enum Foo {Bar}

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

    // Variável `a` corresponde `Foo::Bar`
    if Foo::Bar == a {
    // ^-- isto causa um erro de tempo de compilação. Use `if let`.
        println!("a is foobar");
    }
}

Consulte também:

enum, Option, e o RFC