teoria

Funções e Controle de Fluxo (if/else, loops)

Aprenda sobre funções e controle de fluxo (if/else, loops)

25 min
Aula 5 de 5

🚀 Funções e Controle de Fluxo em Rust

Olá, futuros Rustaceans! 👋 Nesta aula, vamos mergulhar em dois pilares fundamentais de qualquer linguagem de programação: Funções e Controle de Fluxo. São eles que nos permitem organizar nosso código, reutilizá-lo e tomar decisões baseadas em diferentes condições. Em Rust, esses conceitos têm algumas nuances interessantes que os tornam poderosos e expressivos.

Vamos começar nossa jornada! 🛤️


1. Funções: O Coração Reutilizável do Seu Código ❤️

Funções são blocos de código que executam uma tarefa específica e podem ser chamados várias vezes ao longo do programa. Elas nos ajudam a organizar o código, torná-lo mais legível e evitar repetições (o famoso princípio DRY - Don't Repeat Yourself).

1.1. Definição e Chamada de Funções

Em Rust, definimos funções usando a palavra-chave fn.

fn main() {
    println!("Olá do Rust!"); // Chamando uma função nativa
    minha_primeira_funcao(); // Chamando nossa função personalizada
}
 
fn minha_primeira_funcao() {
    println!("Esta é a minha primeira função personalizada!");
}

Explicação:

  • fn: Palavra-chave para declarar uma função.
  • main: É a função especial que é o ponto de entrada de todo programa Rust.
  • minha_primeira_funcao: É o nome que demos à nossa função.
  • (): Parênteses após o nome da função indicam que ela não recebe nenhum parâmetro (argumento).
  • {}: Chaves delimitam o corpo da função, onde o código a ser executado reside.

1.2. Parâmetros e Argumentos 🤝

Funções podem receber valores, chamados de parâmetros, que são variáveis declaradas na assinatura da função. Quando chamamos a função, passamos argumentos para esses parâmetros.

fn main() {
    imprimir_valor(5);
    imprimir_soma(10, 20);
}
 
fn imprimir_valor(x: i32) { // 'x' é o parâmetro, do tipo i32
    println!("O valor é: {}", x);
}
 
fn imprimir_soma(a: i32, b: i32) { // 'a' e 'b' são parâmetros, ambos i32
    println!("A soma de {} e {} é: {}", a, b, a + b);
}

Explicação:

  • Em Rust, você deve declarar o tipo de cada parâmetro na assinatura da função. Isso é uma parte crucial da segurança de tipos do Rust.
  • x: i32 significa que o parâmetro x é do tipo inteiro de 32 bits.

1.3. Valores de Retorno ↩️

Funções podem retornar um valor para o código que as chamou. Em Rust, o tipo de retorno é especificado após uma seta ->. O valor de retorno é a última expressão no corpo da função, ou você pode usar a palavra-chave return.

fn main() {
    let resultado_soma = somar(5, 7);
    println!("A soma é: {}", resultado_soma);
 
    let resultado_multiplicacao = multiplicar(4, 3);
    println!("A multiplicação é: {}", resultado_multiplicacao);
}
 
fn somar(a: i32, b: i32) -> i32 { // -> i32 indica que a função retorna um i32
    a + b // Esta é uma expressão. O valor dela é retornado implicitamente.
}
 
fn multiplicar(x: i32, y: i32) -> i32 {
    return x * y; // Uso explícito de 'return'. Um ponto e vírgula é necessário aqui.
}

Explicação:

  • A última linha de uma função sem ponto e vírgula é considerada uma expressão e seu valor é automaticamente retornado.
  • Se você quiser retornar explicitamente antes do final da função ou em uma condição, use return <valor>;. Note o ponto e vírgula.
  • Funções que não retornam um valor (como main ou imprimir_valor) implicitamente retornam a "unidade" (), que é um tipo vazio.

1.4. Expressões vs. Declarações (Statements) 🧐

Rust é uma linguagem baseada em expressões. Isso significa que a maioria das coisas que você escreve é uma expressão que avalia um valor.

  • Declarações (Statements): Instruções que realizam uma ação, mas não retornam um valor. Ex: let y = 6; ou fn foo() {}.
  • Expressões: Avaliam um valor. Ex: 5 + 6, x, if condition { 5 } else { 6 }.
fn main() {
    let y = 6; // Declaração: 'let y = 6;' é uma declaração.
               // '6' é uma expressão.
 
    let x = { // Este bloco de código é uma expressão!
        let z = 3; // Declaração dentro do bloco
        z + 1      // Expressão: o valor de 'z + 1' (4) é o valor do bloco
    };
 
    println!("O valor de x é: {}", x); // Saída: O valor de x é: 4
}

Exemplo Oficial (Adaptado do The Rust Programming Language Book):

// Exemplo de conversão de Fahrenheit para Celsius
fn main() {
    let fahrenheit = 77.0;
    let celsius = fahrenheit_para_celsius(fahrenheit);
    println!("{}°F é igual a {}°C", fahrenheit, celsius);
 
    let celsius_novo = celsius_para_fahrenheit(25.0);
    println!("25°C é igual a {}°F", celsius_novo);
}
 
fn fahrenheit_para_celsius(temp_f: f64) -> f64 {
    (temp_f - 32.0) * 5.0 / 9.0
}
 
fn celsius_para_fahrenheit(temp_c: f64) -> f64 {
    (temp_c * 9.0 / 5.0) + 32.0
}

2. Controle de Fluxo: Tomando Decisões e Repetindo Ações 🚦

O controle de fluxo permite que seu programa tome decisões sobre qual código executar e quantas vezes repetir uma ação.

2.1. Condicionais com if/else

O construto if permite executar um bloco de código apenas se uma condição for verdadeira. Em Rust, if é uma expressão, o que significa que ele avalia um valor.

fn main() {
    let numero = 7;
 
    if numero < 5 {
        println!("A condição é verdadeira: o número é menor que 5.");
    } else {
        println!("A condição é falsa: o número não é menor que 5.");
    }
 
    // if/else if/else
    let outro_numero = 6;
 
    if outro_numero % 4 == 0 {
        println!("Número é divisível por 4");
    } else if outro_numero % 3 == 0 {
        println!("Número é divisível por 3");
    } else if outro_numero % 2 == 0 {
        println!("Número é divisível por 2");
    } else {
        println!("Número não é divisível por 4, 3 ou 2");
    }
 
    // Usando if em uma declaração 'let' (porque if é uma expressão!)
    let condicao = true;
    let valor = if condicao {
        5
    } else {
        6
    }; // Note que ambos os ramos devem retornar o mesmo tipo!
 
    println!("O valor é: {}", valor); // Saída: O valor é: 5
 
    // Exemplo de erro: tipos diferentes
    // let valor_erro = if condicao {
    //     5
    // } else {
    //     "seis" // Erro! Tipos incompatíveis: i32 vs &str
    // };
}

Explicação:

  • A condição em if não precisa de parênteses, mas o bloco de código if deve ter chaves {}.
  • A condição deve ser do tipo bool. Rust não tenta converter tipos não-booleanos para booleanos.
  • Quando usado em let, ambos os blocos if e else devem retornar valores do mesmo tipo.

2.2. Repetições com Loops 🔄

Rust oferece três tipos de loops: loop, while e for.

2.2.1. loop: O Loop Infinito (e Retorno de Valores)

O loop é o mais simples e cria um loop infinito. Você deve usar break para sair dele. Ele também pode retornar um valor!

fn main() {
    let mut contador = 0;
 
    let resultado = loop {
        contador += 1;
 
        if contador == 10 {
            break contador * 2; // 'break' pode retornar um valor
        }
    };
 
    println!("O resultado do loop é: {}", resultado); // Saída: 20
}

Explicação:

  • loop cria um ciclo que se repetirá indefinidamente até que um break seja encontrado.
  • break: Sai do loop imediatamente. Pode ser usado para retornar um valor do loop.

2.2.2. while: O Loop Condicional

O while executa um bloco de código enquanto uma condição for verdadeira.

fn main() {
    let mut numero = 3;
 
    while numero != 0 {
        println!("{}!", numero);
        numero -= 1;
    }
 
    println!("FIM!");
}

Explicação:

  • A condição é avaliada antes de cada iteração. Se for verdadeira, o bloco é executado. Se for falsa, o loop termina.

2.2.3. for: Iterando sobre Coleções (e Ranges) 🎯

O for loop é usado para iterar sobre os elementos de uma coleção ou um range de números. É o loop mais comum em Rust.

fn main() {
    // Iterando sobre um array
    let a = [10, 20, 30, 40, 50];
 
    for elemento in a.iter() {
        println!("O valor é: {}", elemento);
    }
 
    // Iterando sobre um range (exclusivo no final)
    for numero in 1..4 { // De 1 até 3 (4 é exclusivo)
        println!("Número: {}", numero);
    }
 
    // Iterando sobre um range (inclusivo no final)
    for numero in 1..=4 { // De 1 até 4 (4 é inclusivo)
        println!("Número (inclusivo): {}", numero);
    }
 
    // Exemplo de contagem regressiva com for e range reverso
    for numero in (1..4).rev() { // De 3 até 1
        println!("{}!", numero);
    }
    println!("FIM!");
}

Explicação:

  • a.iter(): Cria um iterador sobre os elementos do array a.
  • 1..4: Cria um range de números de 1 (inclusive) a 4 (exclusive).
  • 1..=4: Cria um range de números de 1 (inclusive) a 4 (inclusive).
  • .rev(): Inverte a ordem do iterador.

2.2.4. Labels de Loop: Controlando Loops Aninhados 🏷️

Você pode usar labels com loops para especificar qual loop você quer break ou continue em loops aninhados.

fn main() {
    let mut contador = 0;
    'loop_externo: loop { // Definindo um label para o loop externo
        println!("Contador externo: {}", contador);
        let mut contador_interno = 0;
 
        'loop_interno: loop { // Definindo um label para o loop interno
            println!("  Contador interno: {}", contador_interno);
            if contador_interno == 2 {
                break 'loop_interno; // Quebra apenas o loop interno
            }
            contador_interno += 1;
        }
 
        if contador == 1 {
            break 'loop_externo; // Quebra o loop externo
        }
        contador += 1;
    }
    println!("Fim de todos os loops.");
}

Explicação:

  • 'nome_do_label: loop { ... } permite nomear um loop.
  • break 'nome_do_label; ou continue 'nome_do_label; controlam o loop específico com aquele label.

3. Integração com Múltiplas Tecnologias (N/A) 🚫

Esta aula foca nos fundamentos da linguagem Rust. A integração com múltiplas tecnologias (como Express.js ou Better-Auth) não se aplica a este tópico específico, pois estamos explorando as funcionalidades básicas e intrínsecas da própria linguagem Rust.


4. Exercícios/Desafios (N/A) 📝

Esta aula é de natureza teórica, focada na explicação conceitual. A seção de exercícios e desafios será abordada em aulas práticas ou projetos futuros, onde você terá a oportunidade de aplicar esses conhecimentos!


5. Resumo e Próximos Passos 🚀

Nesta aula, cobrimos os conceitos essenciais de funções e controle de fluxo em Rust:

  • Funções (fn): Como definir, chamar, passar parâmetros e retornar valores. Entendemos a distinção entre expressões e declarações.
  • Condicionais (if/else): Como tomar decisões no seu código, e a característica de if ser uma expressão em Rust.
  • Loops (loop, while, for): Diferentes maneiras de repetir ações, incluindo loops infinitos, condicionais e iteração sobre coleções, além do uso de labels.

Com esses fundamentos, você já pode começar a escrever programas Rust mais estruturados e dinâmicos!

Próximos Passos: No próximo módulo, vamos explorar os tipos de dados compostos, como tuplas e arrays, e como Rust gerencia a memória com o conceito de Ownership. Prepare-se para mais conceitos poderosos! 💪

© 2025 Escola All Dev. Todos os direitos reservados.

Funções e Controle de Fluxo (if/else, loops) - Curso gratuito de Rust: A linguagem mais amada | escola.all.dev.br