If Let e While Let em Rust
Você já usou o match para tratar Option e Result. Na maioria das vezes, porém, você só se importa de verdade com um padrão e quer ignorar todo o resto. Escrever um match completo com um braço _ => {} só para não fazer nada funciona, mas é cerimônia demais para a situação. O if let é o atalho.
O problema de usar um match completo aqui
Seção intitulada “O problema de usar um match completo aqui”fn main() { let age: Option<i32> = Some(30);
match age { Some(value) => println!("A idade é {value}"), _ => {}, // na verdade, este caso não nos interessa }}Esse _ => {} não faz nada além de satisfazer a regra do match de que “todo caso precisa ser tratado”. Se você só quer agir quando for Some, isso é código demais para uma ideia tão simples.
fn main() { let age: Option<i32> = Some(30);
if let Some(value) = age { println!("A idade é {value}"); }}Saída:
A idade é 30Leia assim: “se age combinar com o padrão Some(value), execute este bloco, com value valendo o que estava dentro”. Se age for None, o bloco é simplesmente pulado: nenhum erro, nada impresso.
Adicionando um else
Seção intitulada “Adicionando um else”O if let pode ter um else, igualzinho a um if comum:
fn main() { let age: Option<i32> = None;
if let Some(value) = age { println!("A idade é {value}"); } else { println!("Nenhuma idade informada"); }}if let com Result
Seção intitulada “if let com Result”Funciona do mesmo jeito com Result, que você conheceu na página de Result:
fn main() { let input = "42";
if let Ok(number) = input.parse::<i32>() { println!("Convertido: {number}"); } else { println!("Não foi possível converter"); }}Encadeando com else if let
Seção intitulada “Encadeando com else if let”Assim como o if tem o else if, o if let pode ser seguido de um else if let. O Rust tenta cada padrão por vez e executa o primeiro bloco que combinar. Isso é útil quando você tem alguns lugares onde procurar um valor, em ordem de preferência:
fn main() { let nickname: Option<&str> = None; let full_name: Option<&str> = Some("Ferris Crab");
if let Some(nick) = nickname { println!("Oi, {nick}!"); } else if let Some(name) = full_name { println!("Olá, {name}."); } else { println!("Olá, desconhecido."); }}Saída:
Olá, Ferris Crab.O nickname (apelido) é None, então o primeiro bloco é pulado. O full_name (nome completo) combina com Some(name), então esse bloco é executado, e o else final nunca é alcançado. Você também pode misturar uma condição comum, como else if is_admin { ... }, em qualquer ponto da cadeia.
Cada variável só existe dentro do próprio bloco: nick não pode ser usada no segundo bloco, e name não pode ser usada depois que a cadeia termina. Se você precisa do valor desempacotado no resto do código, veja o let-else na página de Pattern matching.
while let
Seção intitulada “while let”O while let aplica a mesma ideia a um loop: continue repetindo enquanto um valor combinar com um padrão. Ele é especialmente útil para esvaziar uma coleção:
fn main() { let mut stack = vec![1, 2, 3];
while let Some(top) = stack.pop() { println!("{top}"); }}Saída:
321O .pop() retorna Some(valor) enquanto ainda sobrar alguma coisa, e None quando o vetor fica vazio. Nesse momento, o while let para o loop automaticamente. Não é preciso verificar o tamanho na mão.
if let ou match: qual usar?
Seção intitulada “if let ou match: qual usar?”Use match quando você realmente precisa tratar vários casos de formas diferentes. Prefira o if let quando só existe um padrão que interessa e todo o resto pode ser ignorado. Ele não substitui o match: é só um jeito mais curto de escrever o caso comum de “só me importa um braço”.
Prática
Seção intitulada “Prática”1. Somando uma pilha
Seção intitulada “1. Somando uma pilha”Comece com let mut numbers = vec![4, 8, 15]; e um total acumulado igual a 0. Use while let com .pop() para tirar os números do final, um de cada vez, somar cada um ao total e imprimir o número e o novo total. Dica: o total precisa de mut, e total += n soma ao valor dele.
Ver solução
fn main() { let mut numbers = vec![4, 8, 15]; let mut total = 0;
while let Some(n) = numbers.pop() { total += n; println!("tirei {n}, o total agora é {total}"); }}Saída:
tirei 15, o total agora é 15tirei 8, o total agora é 23tirei 4, o total agora é 27O .pop() tira do final, e é por isso que o 15 sai primeiro.
2. Verificando uma idade a partir de texto
Seção intitulada “2. Verificando uma idade a partir de texto”Guarde let input = "17";. Se ele puder ser convertido em u32, imprima se essa idade é de um adulto (18 ou mais) ou de um menor. Se não puder, imprima que não é um número. Experimente também trocar input por "abc". Dica: coloque um if / else comum dentro do bloco do if let.
Ver solução
fn main() { let input = "17";
if let Ok(age) = input.parse::<u32>() { if age >= 18 { println!("{age} anos: adulto"); } else { println!("{age} anos: menor de idade"); } } else { println!("'{input}' não é um número"); }}Saída:
17 anos: menor de idadeCom input = "abc", a conversão falha, o else de fora é executado e o programa imprime 'abc' não é um número.
if let Padrão = valor { ... }executa o bloco só sevalorcombinar comPadrão; todo o resto é pulado em silêncio.- Adicione um
elsepara tratar o caso que não combina, como em umifcomum, e encadeie vários padrões comelse if let. while let Padrão = valor { ... }repete enquanto o valor continuar combinando, o que é útil para esvaziar uma coleção com.pop().- Use
if let/while letquando só um padrão importa; usematchquando precisar tratar vários casos de formas diferentes.
Teste o que aprendeu
Seção intitulada “Teste o que aprendeu”Teste o que aprendeu
1. O que acontece quando if let Some(value) = age é executado e age é None?
2. Quando você deve preferir match em vez de if let?
3. Em while let Some(top) = stack.pop() { ... }, quando o loop para?
Pontuação: 0 / 3
Para saber mais
Seção intitulada “Para saber mais”- Concise Control Flow with if let and let…else — The Rust Book (em inglês): a introdução oficial, que também explica o let-else.
- if let — Rust by Example (em inglês): exemplos executáveis com
Optione com seus próprios enums, e por que oif leté melhor que omatchquando só um padrão importa. - while let — Rust by Example (em inglês): um loop reescrito de
loop+matchpara umwhile letmais curto. - Padrões
if let— The Rust Reference (em inglês): as regras exatas, inclusive como oif letse relaciona com omatch.