Os três pedaços que todo mundo esquece
Vou direto ao ponto porque já vi muita gente travando em review de código por causa disso. A estrutura de repetição com controle explícito — o for clássico — tem três segmentos obrigatórios: inicialização, condição de continuidade e incremento (ou decremento). Parece óbvio, mas a parte onde os errados acontecem é quase sempre no terceiro segmento ou na interação entre os três. A inicialização define o estado inicial do seu iterador. A condição testa se você deve continuar rodando. O terceiro segmento avança o estado. Se um desses estiver errado, o loop entra em infinito, pula iterações ou para antes da hora. Já perdi umas duas horas debugando um for em Java que parecia correto no papel mas tinha o incremento dentro de uma condição if por engano. O compilador nem reclamava.
com relação aos 3 segmentos de estrutura de repetição
O primeiro segmento é a parte onde você declara e atribui a variável de controle. Em C, por exemplo: for (int i = 0; ...). Esse int é declarado no escopo do próprio for, o que significa que morre quando o loop termina. Em Python isso não existe — o for each age com o iterable diretamente, o que elimina um dos três segmentos tradicionalmente falando. Mas como o assunto aqui é a estrutura clássica de três partes, vou manter o foco. O segundo segmento é puramente booleano. A cada iteração, o runtime avalia essa expressão. Se for verdadeira, executa o corpo. Se falsa, encerra. Já vi devs escreverem algo como i = arr.Length em vez de i arr.Length em linguagens zero-indexadas. O resultado é um IndexError ou OffByOne que aparece só no último elemento. Custo médio de fixação: cinco minutos. Perda de tempo real: duas horas de rastreio.
O terceiro segmento é o mais subestimado. Ele não serve só para somar +1. Pode ser i -= 2, i = i * 2, i = next(i). A flexibilidade aqui é o que diferencia um loop ingênuo de um loop útil. O problema é que muita gente trata esse segmento como decorativo e coloca lógica complexa ali, o que torna o código ilegível ebugueiro. Se o incremento precisa de mais de uma linha, saia do for e use um while com controle explícito. Há um detalhe que poucos mencionam: a ordem de execução dos três segmentos. O initialize roda uma única vez no início. Depois, a cada ciclo, a ordem é: condition body update condition body update, e assim por diante. Se você colocar um side-effect no update que também altera a variável usada na condition, o comportamento pode ficar non-determinístico em algumas linguagens com otimizações agressivas. Compilei um código em Rust uma vez onde o iterador era modificado pelo próprio update e o compilador otimizou o loop de forma inesperada. A solução foi separar o update em uma variável temporária antes do teste condicional.
👉 Clique no botão abaixo para saber mais sobre o assunto!
Outro ponto prático: em loops aninhados, cada for tem seus próprios três segmentos, e eles não compartilham estado. Se você precisa que o inner loop dependa do valor atual do outer loop na inicialização, tudo bem. Mas se o inner loop precisa resetar com base numa mudança no outer durante o corpo, aí você já está num terreno perigoso. O recomendado é extrair o inner for para uma função separada. Fica mais legível e evita bugs de escopo que levam dias para achar. Quando usar while no lugar? Sempre que a condição de parada não pode ser expressa de forma simples no segundo segmento, ou quando o avanço do iterador não é uniforme. Estruturas de dados como árvores e grafos quase sempre se beneficiam de while com pilha explícita. O for de três segmentos brilha em iterações sobre ranges conhecidos — arrays, strings, intervalos numéricos. Fora disso, ele vira trabalho extra.
Uma limitação real dos três segmentos: em linguagem com garbage collection e collections dinâmicas, modificar a coleção durante a iteração do for clássico gera ConcurrentModificationException ou comportamento indefinido. A workaround padrão é iterar sobre uma cópia ou usar um iterator explícito com remove() seguro. Em C++ puro, você pode usar índices e iterar de trás para frente se precisar remover elementos, o que evita o deslocamento de índices que acontece ao remover do início. Para quem quer praticar, não tem material oficial único, mas a documentação de cada linguagem cobre o sintaxe do for. Em C#: docs.microsoft.com/dotnet/csharp/language-reference/keywords/for. Em Java: docs.oracle.com/javase/tutorial/java/nutsandbolts/for.html. Em C: en.cppreference.com/w/c/language/for. O conteúdo é semelhante entre elas, a variação é só na sintaxe e no escopo das variáveis.
O que realmente separa quem domina isso de quem só decorou a sintaxe é a capacidade de prever quantas iterações um loop vai fazer antes de escrevê-lo. Se você consegue olhar para os três segmentos e saber exatamente quantas vezes o corpo vai rodar e em quais valores a variável de controle vai passar, você jáentendeu o suficiente. O resto é prática.