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