O controle de fluxo é fundamental em qualquer linguagem de programação. Em Rust, as estruturas de controle são poderosas e seguras, garantindo que todas as ramificações sejam consideradas. Nesta aula, exploraremos como usar if, else, loops e técnicas avançadas como break com valor e labels de loop.

Diferentemente de muitas linguagens, em Rust if é uma expressão, não uma declaração. Isso significa que ela pode retornar um valor, permitindo código mais conciso e funcional. Além disso, os loops podem ser aninhados e controlados com labels, e o for é uma ferramenta elegante para iterar sobre coleções.

if/else como expressão

Em Rust, if e else são expressões que produzem um valor. Isso permite atribuir o resultado de uma condicional diretamente a uma variável. A sintaxe é similar a outras linguagens, mas a última expressão em cada bloco é o valor retornado.

Por exemplo:

let numero = 7;
let resultado = if numero % 2 == 0 {
    "par"
} else {
    "ímpar"
};
println!("O número {} é {}.", numero, resultado);

Note que ambos os blocos devem retornar valores do mesmo tipo. Caso contrário, o compilador emitirá um erro. Isso garante segurança de tipo.

É possível encadear múltiplas condições com else if:

let nota = 85;
let conceito = if nota >= 90 {
    "A"
} else if nota >= 80 {
    "B"
} else if nota >= 70 {
    "C"
} else {
    "D"
};
println!("Conceito: {}", conceito);

Use if como expressão sempre que possível para tornar o código mais limpo e evitar mutabilidade desnecessária.

loop, while e for

Rust oferece três tipos de loops: loop, while e for. O loop é o mais simples: ele repete um bloco de código indefinidamente até que um break seja encontrado. É útil quando a condição de parada não é simples ou quando precisamos de um loop infinito controlado.

let mut contador = 0;
loop {
    println!("Contagem: {}", contador);
    contador += 1;
    if contador == 5 {
        break;
    }
}

O while executa enquanto uma condição booleana for verdadeira. É ideal quando a condição é verificada antes de cada iteração.

let mut numero = 3;
while numero > 0 {
    println!("{}!", numero);
    numero -= 1;
}
println!("Lançar!");

O for é usado para iterar sobre elementos de uma coleção, como arrays, intervalos ou iteradores. É a forma mais segura e idiomática de percorrer coleções.

let array = [10, 20, 30, 40];
for elemento in array.iter() {
    println!("Valor: {}", elemento);
}

// Usando um intervalo (range)
for numero in 1..5 {
    println!("Número: {}", numero);
}

O for com intervalos é exclusivo e não inclui o limite superior (1..5 produz 1,2,3,4). Use 1..=5 para incluir 5.

break com valor

Uma característica poderosa de Rust é que o break pode retornar um valor do loop. Isso é especialmente útil para loops que precisam produzir um resultado. O valor após a palavra-chave break é retornado como o valor da expressão loop.

let mut contador = 0;
let resultado = loop {
    contador += 1;
    if contador == 10 {
        break contador * 2; // retorna 20
    }
};
println!("Resultado: {}", resultado);

Isso funciona apenas com loop, pois while e for são expressões que retornam () (unidade). O break com valor permite escrever algoritmos que, de outra forma, exigiriam variáveis mutáveis externas.

Exemplo prático: encontrar o primeiro número inteiro que satisfaz uma condição.

let numero = loop {
    let n = rand::random::<i32>(); // supondo crate rand
    if n > 0 && n % 7 == 0 {
        break n;
    }
};
println!("Número encontrado: {}", numero);

Labels de loop

Quando loops são aninhados, labels permitem especificar qual loop o break ou continue deve afetar. Uma label é um identificador seguido de dois pontos, colocado antes do loop.

'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);
    }
}

Sem a label, o break sairia apenas do loop interno. Com a label, podemos sair de qualquer nível. Da mesma forma, continue pode ser usado com labels para pular iterações em loops externos.

Labels são úteis em situações como busca em matrizes, onde ao encontrar um elemento, desejamos interromper a busca completamente.

let matriz = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
let mut encontrado = false;
'busca: for linha in &matriz {
    for &valor in linha {
        if valor == 5 {
            encontrado = true;
            break 'busca;
        }
    }
}
println!("Encontrado: {}", encontrado);

Referências

Exercícios

  1. Escreva um programa que use if como expressão para atribuir a uma variável a string "maior" se um número for maior que 10, ou "menor ou igual" caso contrário. Imprima o resultado.
  2. ✓ Resposta:
    fn main() {
        let numero = 15;
        let resultado = if numero > 10 { "maior" } else { "menor ou igual" };
        println!("O número é {} que 10.", resultado);
    }
  3. Use um loop while para imprimir os números de 1 a 5, cada um em uma linha.
  4. ✓ Resposta:
    fn main() {
        let mut i = 1;
        while i <= 5 {
            println!("{}", i);
            i += 1;
        }
    }
  5. Crie um loop for que itere sobre um array de nomes e imprima cada nome em maiúsculas.
  6. ✓ Resposta:
    fn main() {
        let nomes = ["ana", "joão", "maria"];
        for nome in nomes.iter() {
            println!("{}", nome.to_uppercase());
        }
    }
  7. Escreva uma função que receba um número e use um loop loop com break retornando o fatorial desse número.
  8. ✓ Resposta:
    fn fatorial(n: u32) -> u32 {
        let mut acc = 1;
        let mut i = 1;
        loop {
            if i > n {
                break acc;
            }
            acc *= i;
            i += 1;
        }
    }
    
    fn main() {
        println!("Fatorial de 5: {}", fatorial(5));
    }
  9. Use labels de loop para encontrar o primeiro par (i, j) com i de 1 a 3 e j de 1 a 3 tal que i * j > 4. Imprima o par e pare todos os loops.
  10. ✓ Resposta:
    fn main() {
        'externo: for i in 1..=3 {
            for j in 1..=3 {
                if i * j > 4 {
                    println!("Par: ({}, {})", i, j);
                    break 'externo;
                }
            }
        }
    }