Controle de fluxo: if, loop, while e for
1. Estruturas condicionais com if e else
Em Rust, o if é uma expressão que não exige parênteses ao redor da condição, mas exige blocos com chaves. A sintaxe é limpa e direta:
let temperatura = 30;
if temperatura > 25 {
println!("Está quente!");
} else if temperatura > 15 {
println!("Temperatura agradável.");
} else {
println!("Está frio.");
}
Uma das características mais poderosas do if em Rust é que ele pode ser usado como expressão para atribuição de variáveis:
let nota = 85;
let conceito = if nota >= 90 {
"A"
} else if nota >= 80 {
"B"
} else if nota >= 70 {
"C"
} else {
"D"
};
println!("Conceito: {}", conceito); // Conceito: B
Note que todos os braços do if devem retornar valores do mesmo tipo — caso contrário, o compilador emitirá um erro.
2. Controle avançado com if let
O if let é uma construção sintática que combina pattern matching com uma condicional, ideal para desestruturar Option ou Result de forma concisa:
let valor: Option<i32> = Some(42);
if let Some(x) = valor {
println!("Valor encontrado: {}", x);
} else {
println!("Nenhum valor presente.");
}
Comparado com match, o if let é mais enxuto quando você só se importa com um único padrão:
// Com match
match valor {
Some(x) => println!("{}", x),
_ => {}
}
// Com if let (mais conciso)
if let Some(x) = valor {
println!("{}", x);
}
Também é possível encadear else if let:
let dado: Result<i32, &str> = Err("falhou");
if let Ok(val) = dado {
println!("Sucesso: {}", val);
} else if let Err(msg) = dado {
println!("Erro: {}", msg);
}
3. Laço infinito e controlado com loop
O loop cria um laço infinito que só termina com break:
let mut contador = 0;
loop {
contador += 1;
if contador == 5 {
break;
}
if contador % 2 == 0 {
continue; // pula para a próxima iteração
}
println!("Ímpar: {}", contador);
}
Uma característica única do loop em Rust é a capacidade de retornar valores com break:
let mut tentativas = 0;
let resultado = loop {
tentativas += 1;
if tentativas >= 3 {
break tentativas; // retorna o valor
}
};
println!("Número de tentativas: {}", resultado); // 3
4. Laço condicional com while
O while executa um bloco enquanto uma condição booleana for verdadeira:
let mut contagem = 3;
while contagem > 0 {
println!("{}...", contagem);
contagem -= 1;
}
println!("Fogo!");
A diferença principal entre while e loop com break é que o while já embute a condição de parada na própria estrutura, tornando o código mais legível quando a condição é simples.
O while let funciona de forma similar ao if let, mas em um laço:
let mut pilha = vec![1, 2, 3, 4, 5];
while let Some(topo) = pilha.pop() {
println!("Removido: {}", topo);
}
// Remove todos os elementos até a pilha ficar vazia
5. Iteração com for
O for em Rust é extremamente eficiente e idiomático para iterações:
// Intervalo (range) exclusivo no final
for i in 0..5 {
println!("{}", i); // 0, 1, 2, 3, 4
}
// Intervalo inclusivo
for i in 0..=5 {
println!("{}", i); // 0, 1, 2, 3, 4, 5
}
Para iterar sobre coleções, use métodos de iteração:
let numeros = [10, 20, 30, 40, 50];
// Iteração sobre referências (não consome a coleção)
for num in numeros.iter() {
println!("{}", num);
}
// Iteração com posse (consome a coleção)
let palavras = vec!["olá", "mundo"];
for palavra in palavras.into_iter() {
println!("{}", palavra);
}
// palavras não pode mais ser usado aqui
// Iteração mutável
let mut valores = vec![1, 2, 3];
for valor in valores.iter_mut() {
*valor *= 2;
}
println!("{:?}", valores); // [2, 4, 6]
6. Controle de fluxo em loops: break e continue
Rust permite rotular loops para controle preciso em estruturas aninhadas:
'externo: for i in 1..=3 {
'interno: for j in 1..=3 {
if i == 2 && j == 2 {
break 'externo; // Sai do loop externo
}
println!("i={}, j={}", i, j);
}
}
// Saída: i=1, j=1 i=1, j=2 i=1, j=3 i=2, j=1
O continue com rótulos pula iterações específicas:
'loop_externo: for i in 0..3 {
for j in 0..3 {
if i == 1 {
continue 'loop_externo; // Pula para próxima iteração do externo
}
println!("({}, {})", i, j);
}
}
// Saída: (0,0) (0,1) (0,2) (2,0) (2,1) (2,2)
Boas práticas: use rótulos com moderação — eles podem tornar o código difícil de seguir. Prefira refatorar loops aninhados complexos em funções separadas.
7. Comparação e boas práticas entre os loops
| Loop | Uso principal | Quando usar |
|---|---|---|
loop |
Laços infinitos ou que precisam retornar valor | Quando a condição de parada é complexa ou depende de lógica interna |
while |
Laços com condição booleana simples | Quando a condição é verificada apenas no início |
for |
Iteração sobre coleções ou intervalos | Preferido na maioria dos casos por segurança e legibilidade |
Performance: O for sobre intervalos é geralmente tão eficiente quanto while manual, pois o compilador otimiza bem. Use for como padrão.
Erros comuns:
- Loops infinitos acidentais: sempre verifique se a condição de parada será atingida
- Borrowing em iterações: ao iterar sobre uma coleção com for, o Rust aplica regras de empréstimo. Use .iter() para leitura e .iter_mut() para modificação
// Erro comum: tentar modificar coleção durante iteração
let mut itens = vec![1, 2, 3];
for item in &itens { // ou itens.iter()
// itens.push(4); // ERRO: borrow mutável e imutável simultâneo
}
Recomendação geral: Prefira for para iterações, while para laços condicionais simples, e loop apenas quando precisar de um laço infinito ou retorno de valor.
Referências
- The Rust Programming Language - Control Flow — Capítulo oficial do livro sobre if, loop, while e for em Rust
- Rust by Example - Flow Control — Exemplos práticos de todas as estruturas de controle de fluxo
- Rust Reference - If and if let expressions — Documentação detalhada da sintaxe de if e if let
- Rust Reference - Loop expressions — Especificação completa de loop, while, for e rótulos
- Rust Lang Blog - The Rust Way: Control Flow — Artigo técnico sobre evoluções no controle de fluxo em Rust (inclui exemplos com if let e while let)
- Exercism - Rust Track: Control Flow — Tutoriais interativos e exercícios sobre controle de fluxo em Rust