Funções em Rust: Os Blocos de Construção do Código

Funções são essenciais em Rust. Elas são declaradas com a palavra-chave fn e servem como a estrutura principal para o código, sendo a função main o ponto de entrada de qualquer programa.

Rust utiliza o padrão snake case para nomes de funções e variáveis, onde todas as letras são minúsculas e as palavras são separadas por sublinhados (ex: calcular_total).

Aqui está um exemplo simples de definição e chamada de função:

fn main() {
    println!("Início do programa.");

    saudacao();
    // A ordem de execução segue a ordem das chamadas dentro de `main`.
}

fn saudacao() {
    println!("Olá, Rustacean!");
    // A função é definida usando `fn nome_funcao() { ... }`.
}

Ao rodar, o código exibe:

Início do programa.
Olá, Rustacean!

Parâmetros em Funções

As funções podem receber parâmetros (valores de entrada) que são definidos em sua assinatura. Em Rust, é obrigatório declarar o tipo de cada parâmetro.

Isso é fundamental: informar o tipo ajuda o compilador a garantir que o código esteja correto e fornece mensagens de erro muito mais claras.

Exemplo com um parâmetro:

fn main() {
    imprimir_numero(10);
    // 10 é o argumento passado para o parâmetro `x`.
}

fn imprimir_numero(x: i32) {
    // Declara o parâmetro 'x' como um inteiro de 32 bits (i32).
    println!("O número fornecido é: {x}");
}

Exemplo com múltiplos parâmetros:

Quando há mais de um, separe-os por vírgula e declare o tipo de cada um.

fn main() {
    calcular_area(2.5, 4.0);
}

fn calcular_area(base: f64, altura: f64) {
    // Ambos os parâmetros são do tipo 'f64' (ponto flutuante).
    let area = base * altura;
    println!("A área do retângulo é: {area}");
}

Statements e Expressions

O corpo das funções em Rust é composto por statements (declarações) e expressions (expressões), e entender a diferença é crucial:

  • Expressions (Expressões): Avaliam para um valor resultante. Elas formam a maior parte do código. Exemplos: uma operação matemática (1 + 2), a chamada de uma função, ou um bloco de código entre chaves.

Statements (Declarações): São instruções que executam uma ação e não retornam valor. Exemplos: definir uma variável com let ou a própria definição de uma função.

let y = 6; é uma declaração.

Blocos de Código como Expressões:

Um bloco de código {...} pode ser uma expressão, e o valor retornado é o da última linha, desde que ela não termine com ponto e vírgula.

fn main() {
    let z = {
        let a = 10; // Statement
        a + 5       // Expression (retorna o valor 15)
    };
    println!("O valor de Z é: {z}"); // Saída: 15
}

Funções com Retorno de Valor

Funções podem devolver um valor para quem as chamou. Para isso:

  1. Declare o tipo de retorno na assinatura após uma seta (->).
  2. O valor retornado implicitamente é o da última expression no corpo da função (sem ponto e vírgula).

Você pode usar explicitamente a palavra-chave return, mas a prática comum em Rust é usar a expressão final.

Exemplo de Retorno:

fn main() {
    let resultado = dobro(8);
    println!("O dobro é: {resultado}"); // Saída: 16
}

fn dobro(numero: i32) -> i32 {
    // Declara que a função retorna um i32.
    numero * 2 // Expressão final (SEM ponto e vírgula)
}

Cuidado com o Ponto e Vírgula

Se você colocar um ponto e vírgula na expressão final, ela se transforma em um statement e não retorna um valor. O compilador Rust representa a ausência de valor com o tipo unit (()), o que gera um erro se a função esperava, por exemplo, um i32:

fn erro_de_retorno(valor: i32) -> i32 {
    valor + 1; // Com ponto e vírgula, isso se torna um Statement.
    // O Rust retorna implicitamente '()' (unit), não 'i32'.
}
// Isso causaria um erro de 'mismatched types' (tipos incompatíveis).