Capítulo 03: Controle de Fluxo: if/else, loop, while e for

Especialização em Sistemas com Rust • Rust 2021 Edition • Ownership, Concorrência Segura e Sistemas de Alta Performance


🗺️ Mapa Conceitual de Estruturas de Decisão e Laços de Repetição

flowchart TD
    Fluxo["Controle de Fluxo em Rust (Expressões Avaliadas)"] --> Ramificacao["Decisão Condicional"]
    Fluxo --> Repeticao["Laços de Repetição"]

    subgraph RAMIFICACAO_DETALHE["Decisão: if / else if / else"]
        IfExpr["if condicao { exp1 } else { exp2 }"]
        TipagemRamos["Ramos Devem Retornar Exatamente o Mesmo Tipo T"]
        ZeroTernario["Dispensa Operador Ternário (? :) de C/JS"]
    end

    subgraph REPETICAO_DETALHE["Mecanismos de Repetição"]
        LoopIncondicional["loop: Loop Infinito com Retorno 'break valor'"]
        LabeledLoops["'label: Rótulos para Interromper Laços Aninhados"]
        WhileCond["while condicao { ... }: Loop Condicional"]
        ForIter["for item in iterador: Seguro contra 'Off-by-One' e OOB"]
    end

    Ramificacao --- RAMIFICACAO_DETALHE
    Repeticao --- REPETICAO_DETALHE

    style Fluxo fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px
    style Ramificacao fill:#fff3e0,stroke:#ff9800,stroke-width:2px
    style Repeticao fill:#ede7f6,stroke:#7e57c2,stroke-width:2px
    style LoopIncondicional fill:#e8f5e9,stroke:#4caf50,stroke-width:2px

🏛️ 1. O Paradigma de Expressões no Controle de Fluxo

Em linguagens como C, C++, Java e JavaScript, estruturas como if/else e laços de repetição são declarações (statements), ou seja, blocos de código que executam ações imperativas mas não produzem um valor de saída direto. Para atribuir um resultado condicional, essas linguagens recorrem a operadores ternários (cond ? a : b) ou mutabilidade de variáveis externas.

No Rust, quase tudo é uma expressão (expression):

  • O bloco if/else retorna um valor diretamente para ser atribuído a uma constante ou variável.
  • O compilador verifica se todos os ramos possíveis retornam rigorosamente o mesmo tipo de dado.
  • Não existe operador ternário em Rust porque o próprio if/else já cumpre essa função com sintaxe limpa e tipagem estrita.

⚙️ 2. A Estrutura Condicional if/else como Expressão

Veja como inicializar variáveis imutáveis com base em condições de negócio sem recorrer a mut:

let pressao_caldeira_psi = 142.5;

// Atribuição direta do resultado da expressão if/else
let estado_valvula: &str = if pressao_caldeira_psi > 150.0 {
    "ABERTURA_EMERGENCIA"
} else if pressao_caldeira_psi > 120.0 {
    "ALIVIO_PARCIAL"
} else {
    "FECHADA"
};

println!("Comando enviado à válvula: {}", estado_valvula);

2.1 Garantia de Consistência de Tipos entre Ramos

Se um ramo retornar um inteiro e outro retornar uma string, o compilador rejeitará o código com o erro E0308: if and else have incompatible types. Essa restrição impede estados ambíguos de memória.


🔄 3. Os Três Laços de Repetição: loop, while e for

3.1 loop: Laço Infinito com Retorno de Valor pelo break

O comando loop instrui o compilador a gerar um laço sem condição de parada inicial. Ele é ideal para loops de eventos de polling, servidores de rede e rotinas de retentativa (retries). Além disso, o break pode carregar um valor que se torna o resultado da expressão:

let mut contador_retries = 0;

let conexao_id: u32 = loop {
    contador_retries += 1;
    
    if tentar_handshake() {
        // break com expressão: define o valor final de 'conexao_id'
        break contador_retries * 1024;
    }

    if contador_retries >= 5 {
        break 0; // Código de falha
    }
};

3.2 Rótulos de Loop (Loop Labels) para Laços Aninhados

Quando se processam matrizes multidimensionais ou tabelas de dados aninhadas, interromper o laço externo a partir do laço interno costumava exigir flags booleanas auxiliares. O Rust resolve isso com rótulos identificados por apóstrofo ('nome):

'busca_matriz: for x in 0..10 {
    for y in 0..10 {
        if matriz[x][y] == VALOR_CRITICO {
            println!("Elemento encontrado em ({}, {}). Encerrando busca total.", x, y);
            break 'busca_matriz; // Interrompe o laço externo identificado
        }
    }
}

3.3 while: Repetição Condicional

Executa o bloco enquanto a condição for verdadeira. Se a condição for falsa na primeira avaliação, o bloco não executa nenhuma vez:

let mut bateria_pct = 100;
while bateria_pct > 15 {
    executar_ciclo_drone();
    bateria_pct -= 5;
}
println!("Bateria baixa. Iniciando pouso automático.");

3.4 for: A Maneira Idiomática e Segura de Iterar

Em Rust, você quase nunca deve usar while ou contadores manuais com índices para percorrer coleções. O laço for consome iteradores nativos, eliminando completamente erros de Off-by-One e checagens de limite em tempo de execução (Bounds Checking Elimination):

// 1. Ranges exclusivos [0, 5) -> 0 a 4
for i in 0..5 {
    println!("Índice: {}", i);
}

// 2. Ranges inclusivos [1, 5] -> 1 a 5
for segundo in 1..=5 {
    println!("Segundo: {}", segundo);
}

// 3. Iterador reverso
for contagem in (1..=3).rev() {
    println!("{}...", contagem);
}

// 4. Iteração direta sobre coleções sem risco de IndexOutOfBounds
let sensores = ["TEMP_EXT", "HUMID_01", "BAROM_02"];
for sensor in sensores {
    println!("Lendo sensor: {}", sensor);
}

⚖️ 4. Matriz Comparativa: loop vs while vs for

Característica loop while for
Condição Prévia Nenhuma (incondicional) Avaliada antes de cada ciclo Baseada em iterador
Retorno de Valor via break SIM (break valor;) Não (retorna ()) Não (retorna ())
Garantia de Término Depende de break explícito Depende de condição mutável Garante término sobre coleções finitas
Caso de Uso Ideal Polling, Retries, Servidores Condições com múltiplos fatores Percorrer listas, vetores e ranges numéricos

⚠️ 5. Armadilhas Comuns e Mensagens do Compilador

  1. Esquecer o Ramo else quando if é Expressão de Atribuição:
    let status = if ativo { "ON" }; // ERRO: `if` may be missing an `else` clause
    

    Explicação: Se a condição for falsa, a variável ficaria sem valor definido, o que é expressamente proibido pela segurança do Rust. Todo if de atribuição deve ter obrigatoriamente um else.

  2. Usar for i in 0..vetor.len():
    • Anti-Pattern: Acessar vetor[i] dentro do loop força o compilador a checar os limites do array a cada iteração.
    • Correção Idiomática: Use for item in &vetor ou for (i, item) in vetor.iter().enumerate().

🛡️ 6. Boas Práticas de Engenharia em Rust

  • Imutabilidade Sempre: Prefira inicializar valores com let x = if ... else ...; a declarar let mut x; e preenchê-la dentro de um bloco condicional imperativo.
  • Aproveite a Eliminação de Bounds Check: Ao usar laços for sobre iteradores, o compilador do Rust remove instruções assembly de verificação de limites do código final gerado, tornando seu loop tão ou mais rápido que código C otimizado.

📋 7. Cheat-Sheet: Resumo Executivo

// if/else como expressão
let modo = if debug { "VERBOSE" } else { "SILENT" };

// loop com valor
let res = loop { if pronto() { break 42; } };

// Laço rotulado
'externo: for x in 0..5 { break 'externo; }

// Range inclusivo e reverso
for n in (1..=10).rev() { /* ... */ }