Funções: parâmetros, retorno e expressões vs statements

1. Introdução às Funções em Rust

Em Rust, funções são blocos de código reutilizáveis que realizam tarefas específicas. A sintaxe básica de declaração utiliza a palavra-chave fn, seguida pelo nome da função em snake_case (convenção obrigatória), parênteses para parâmetros e um bloco de código entre chaves.

fn saudacao() {
    println!("Olá, Rust!");
}

fn main() {
    saudacao();
}

O ponto de entrada de todo programa Rust é a função fn main(), que não possui parâmetros e retorna implicitamente uma tupla vazia ().

2. Parâmetros de Função

Em Rust, todos os parâmetros de função são obrigatórios e exigem anotação explícita de tipo. Diferente de linguagens como JavaScript ou Python, não existem parâmetros opcionais ou valores padrão nativos.

fn soma(a: i32, b: i32) {
    println!("A soma é: {}", a + b);
}

fn main() {
    soma(10, 20);
}

Múltiplos parâmetros são separados por vírgula. A passagem pode ser por valor (transferindo ownership) ou por referência (emprestando o valor), conceito que exploraremos mais adiante.

fn exibe_nome(nome: &String) {
    println!("Nome: {}", nome);
}

3. Retorno de Valores

Funções em Rust podem retornar valores utilizando a sintaxe -> Tipo. Existem duas formas principais de retorno:

Retorno implícito — a última expressão do bloco, sem ponto e vírgula, é automaticamente retornada:

fn quadrado(x: i32) -> i32 {
    x * x  // sem ponto e vírgula
}

fn main() {
    let resultado = quadrado(5);
    println!("5 ao quadrado é: {}", resultado);
}

Retorno explícito — utilizando a palavra-chave return, útil para saídas antecipadas:

fn maior(a: i32, b: i32) -> i32 {
    if a > b {
        return a;
    }
    b
}

Para retornar múltiplos valores, use tuplas:

fn dividir(dividendo: i32, divisor: i32) -> (i32, i32) {
    (dividendo / divisor, dividendo % divisor)
}

fn main() {
    let (quociente, resto) = dividir(17, 5);
    println!("Quociente: {}, Resto: {}", quociente, resto);
}

4. Expressões vs Statements — A Distinção Central

Esta é a distinção mais importante para entender funções em Rust. Tudo em Rust é classificado como:

  • Expressão: produz um valor e pode ser composta por outras expressões.
  • Statement: executa uma ação, mas não retorna valor.
fn main() {
    // Statement: declaração de variável
    let x = 5;

    // Expressão: 5 + 5 produz o valor 10
    let y = 5 + 5;

    // Statement: chamada de função
    println!("x = {}", x);
}

O ponto e vírgula é o divisor de águas: ele converte uma expressão em um statement. Sem ponto e vírgula, o código é uma expressão que retorna valor; com ponto e vírgula, vira um statement que não retorna nada.

fn main() {
    // Bloco como expressão
    let valor = {
        let x = 3;
        x + 1  // expressão, retorna 4
    };
    println!("Valor: {}", valor); // 4

    // Bloco como statement
    {
        let x = 3;
        x + 1;  // statement, não retorna nada
    };
}

5. Corpo de Função como Expressão

O corpo de uma função é um bloco { }, que funciona como uma expressão. O último valor do bloco (sem ponto e vírgula) é retornado automaticamente.

fn classificar_nota(nota: u8) -> &'static str {
    if nota >= 90 {
        "A"
    } else if nota >= 80 {
        "B"
    } else {
        "C"
    }
}

if e match também são expressões em Rust, permitindo atribuições condicionais elegantes:

fn main() {
    let numero = 7;
    let par_ou_impar = if numero % 2 == 0 { "par" } else { "ímpar" };
    println!("{} é {}", numero, par_ou_impar);

    let descricao = match numero {
        1 => "um",
        2 => "dois",
        _ => "outro",
    };
}

6. Funções com Parâmetros de Referência e Mutabilidade

Rust possui regras estritas de ownership. Para evitar a transferência de propriedade, usamos referências:

Referência imutável (&T) — permite ler o valor sem modificar:

fn calcular_tamanho(s: &String) -> usize {
    s.len()
}

fn main() {
    let texto = String::from("Rust");
    let tamanho = calcular_tamanho(&texto);
    println!("'{}' tem {} caracteres", texto, tamanho); // texto ainda é válido
}

Referência mutável (&mut T) — permite modificar o valor emprestado:

fn adicionar_ponto(s: &mut String) {
    s.push_str("!");
}

fn main() {
    let mut saudacao = String::from("Olá");
    adicionar_ponto(&mut saudacao);
    println!("{}", saudacao); // "Olá!"
}

Regra fundamental: você pode ter várias referências imutáveis ou uma única referência mutável em um mesmo escopo, nunca ambas simultaneamente.

7. Funções Aninhadas e Escopo

Rust permite declarar funções dentro de funções, mas com limitações importantes:

fn externa() {
    fn interna() {
        println!("Dentro da função interna");
    }
    interna();
}

fn main() {
    externa();
    // interna(); // ERRO! não está no escopo
}

Funções aninhadas não capturam variáveis do escopo externo. Para isso, utilizamos closures:

fn main() {
    let fator = 2;
    let multiplicador = |x: i32| x * fator; // closure captura 'fator'
    println!("{}", multiplicador(5)); // 10
}

8. Boas Práticas e Erros Comuns

Erro 1: Esquecer ponto e vírgula no retorno implícito

fn erro() -> i32 {
    42; // Erro: retorna (), não i32
}

Erro 2: Confundir () com ausência de retorno

Toda função que não especifica retorno retorna () (tupla vazia). Isso é diferente de "não retornar nada".

fn ok() {} // retorna ()
fn explicito() -> () {} // equivalente

Erro 3: Uso excessivo de return

Prefira retorno implícito para funções curtas:

// Ruim
fn soma(a: i32, b: i32) -> i32 {
    return a + b;
}

// Bom
fn soma(a: i32, b: i32) -> i32 {
    a + b
}

Dicas de estilo:
- Use rustfmt para formatação consistente
- Prefira funções pequenas e focadas
- Documente com /// para gerar documentação automática

/// Calcula a área de um retângulo
fn area_retangulo(largura: u32, altura: u32) -> u32 {
    largura * altura
}

Conclusão

Dominar funções em Rust requer compreender a diferença fundamental entre expressões e statements, saber quando usar retorno implícito vs explícito, e entender como parâmetros interagem com o sistema de ownership. Esses conceitos formam a base para escrever código idiomático e seguro em Rust.

Referências